Skip to content

Fix conditional types with infer clause in TypeScript declaration generation #1382

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions testdata/baselines/reference/compiler/callVsFunctionSignature.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//// [tests/cases/compiler/callVsFunctionSignature.ts] ////

//// [callVsFunctionSignature.ts]
// Function type parenthesized
type Test1<T> = T extends (() => infer R) ? R : never;

// Call signature in type literal
type Test2<T> = T extends { (): infer R } ? R : never;

//// [callVsFunctionSignature.js]


//// [callVsFunctionSignature.d.ts]
// Function type parenthesized
type Test1<T> = T extends (() => infer R) ? R : never;
// Call signature in type literal
type Test2<T> = T extends {
(): infer R;
} ? R : never;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//// [tests/cases/compiler/callVsFunctionSignature.ts] ////

=== callVsFunctionSignature.ts ===
// Function type parenthesized
type Test1<T> = T extends (() => infer R) ? R : never;
>Test1 : Symbol(Test1, Decl(callVsFunctionSignature.ts, 0, 0))
>T : Symbol(T, Decl(callVsFunctionSignature.ts, 1, 11))
>T : Symbol(T, Decl(callVsFunctionSignature.ts, 1, 11))
>R : Symbol(R, Decl(callVsFunctionSignature.ts, 1, 38))
>R : Symbol(R, Decl(callVsFunctionSignature.ts, 1, 38))

// Call signature in type literal
type Test2<T> = T extends { (): infer R } ? R : never;
>Test2 : Symbol(Test2, Decl(callVsFunctionSignature.ts, 1, 54))
>T : Symbol(T, Decl(callVsFunctionSignature.ts, 4, 11))
>T : Symbol(T, Decl(callVsFunctionSignature.ts, 4, 11))
>R : Symbol(R, Decl(callVsFunctionSignature.ts, 4, 37))
>R : Symbol(R, Decl(callVsFunctionSignature.ts, 4, 37))

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//// [tests/cases/compiler/callVsFunctionSignature.ts] ////

=== callVsFunctionSignature.ts ===
// Function type parenthesized
type Test1<T> = T extends (() => infer R) ? R : never;
>Test1 : Test1<T>

// Call signature in type literal
type Test2<T> = T extends { (): infer R } ? R : never;
>Test2 : Test2<T>

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//// [tests/cases/compiler/conditionalTypeWithInferInConstructor.ts] ////

//// [conditionalTypeWithInferInConstructor.ts]
// This is the exact case from issue #1379
type ExtractReturn<T> = T extends { new(): infer R } ? R : never;

//// [conditionalTypeWithInferInConstructor.js]


//// [conditionalTypeWithInferInConstructor.d.ts]
// This is the exact case from issue #1379
type ExtractReturn<T> = T extends {
new ();
} ? R : never;


//// [DtsFileErrors]


conditionalTypeWithInferInConstructor.d.ts(4,5): error TS2304: Cannot find name 'R'.


==== conditionalTypeWithInferInConstructor.d.ts (1 errors) ====
// This is the exact case from issue #1379
type ExtractReturn<T> = T extends {
new ();
} ? R : never;
~
!!! error TS2304: Cannot find name 'R'.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//// [tests/cases/compiler/conditionalTypeWithInferInConstructor.ts] ////

=== conditionalTypeWithInferInConstructor.ts ===
// This is the exact case from issue #1379
type ExtractReturn<T> = T extends { new(): infer R } ? R : never;
>ExtractReturn : Symbol(ExtractReturn, Decl(conditionalTypeWithInferInConstructor.ts, 0, 0))
>T : Symbol(T, Decl(conditionalTypeWithInferInConstructor.ts, 1, 19))
>T : Symbol(T, Decl(conditionalTypeWithInferInConstructor.ts, 1, 19))
>R : Symbol(R, Decl(conditionalTypeWithInferInConstructor.ts, 1, 48))
>R : Symbol(R, Decl(conditionalTypeWithInferInConstructor.ts, 1, 48))

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//// [tests/cases/compiler/conditionalTypeWithInferInConstructor.ts] ////

=== conditionalTypeWithInferInConstructor.ts ===
// This is the exact case from issue #1379
type ExtractReturn<T> = T extends { new(): infer R } ? R : never;
>ExtractReturn : ExtractReturn<T>

7 changes: 7 additions & 0 deletions testdata/tests/cases/compiler/callVsConstructorSig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// @target: esnext
// @module: preserve
// @declaration: true

// Direct comparison: call signature vs constructor signature
type CallSig<T> = T extends { (): infer R } ? R : never;
type ConstructorSig<T> = T extends { new(): infer R } ? R : never;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// @target: esnext
// @module: preserve
// @declaration: true

// This is the exact case from issue #1379
type ExtractReturn<T> = T extends { new(): infer R } ? R : never;
7 changes: 7 additions & 0 deletions testdata/tests/cases/compiler/constructorSignatureInfer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// @declaration: true
// Test case to isolate the constructor signature issue
type ExtractConstructorReturn<T> = T extends { new(): infer R } ? R : never;

export function test(): ExtractConstructorReturn<{ new(): string }> {
return "" as any;
}
23 changes: 23 additions & 0 deletions testdata/tests/cases/compiler/inferPatternComparison.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// @target: esnext
// @module: preserve
// @declaration: true

// Test various patterns to understand where the issue occurs

// 1. Function parameter (works)
type Test1<T> = T extends (x: infer R) => any ? R : never;

// 2. Function return type (should work)
type Test2<T> = T extends () => infer R ? R : never;

// 3. Constructor signature return type (broken)
type Test3<T> = T extends { new(): infer R } ? R : never;

// 4. Constructor parameter (might be broken too)
type Test4<T> = T extends { new(x: infer R): any } ? R : never;

// 5. Call signature return type (should work like function)
type Test5<T> = T extends { (): infer R } ? R : never;

// 6. Call signature parameter (should work like function)
type Test6<T> = T extends { (x: infer R): any } ? R : never;
13 changes: 13 additions & 0 deletions testdata/tests/cases/compiler/sideBySideComparison.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// @target: esnext
// @module: preserve
// @declaration: true

// Call signature in type literal - should work
type ExtractCallReturn<T> = T extends { (): infer R } ? R : never;

// Constructor signature in type literal - should be fixed
type ExtractConstructReturn<T> = T extends { new(): infer R } ? R : never;

// Test both with same usage
declare const callTest: ExtractCallReturn<() => string>;
declare const constructTest: ExtractConstructReturn<{ new(): string }>;
6 changes: 6 additions & 0 deletions testdata/tests/cases/compiler/simpleInferFunction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// @target: esnext
// @module: preserve
// @declaration: true

// Simple function parameter case
type ExtractParam<T> = T extends (x: infer R) => any ? R : never;