Skip to content

Commit e49db97

Browse files
authored
Exclude parameters of non-inferrable signatures from inference (#53756)
1 parent b92483f commit e49db97

File tree

5 files changed

+119
-7
lines changed

5 files changed

+119
-7
lines changed

src/compiler/checker.ts

+9-7
Original file line numberDiff line numberDiff line change
@@ -25026,12 +25026,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2502625026
}
2502725027

2502825028
function inferFromSignature(source: Signature, target: Signature) {
25029-
const saveBivariant = bivariant;
25030-
const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown;
25031-
// Once we descend into a bivariant signature we remain bivariant for all nested inferences
25032-
bivariant = bivariant || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.MethodSignature || kind === SyntaxKind.Constructor;
25033-
applyToParameterTypes(source, target, inferFromContravariantTypesIfStrictFunctionTypes);
25034-
bivariant = saveBivariant;
25029+
if (!(source.flags & SignatureFlags.IsNonInferrable)) {
25030+
const saveBivariant = bivariant;
25031+
const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown;
25032+
// Once we descend into a bivariant signature we remain bivariant for all nested inferences
25033+
bivariant = bivariant || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.MethodSignature || kind === SyntaxKind.Constructor;
25034+
applyToParameterTypes(source, target, inferFromContravariantTypesIfStrictFunctionTypes);
25035+
bivariant = saveBivariant;
25036+
}
2503525037
applyToReturnTypes(source, target, inferFromTypes);
2503625038
}
2503725039

@@ -35783,7 +35785,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3578335785
return links.contextFreeType;
3578435786
}
3578535787
const returnType = getReturnTypeFromBody(node, checkMode);
35786-
const returnOnlySignature = createSignature(/*declaration*/ undefined, /*typeParameters*/ undefined, /*thisParameter*/ undefined, emptyArray, returnType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None);
35788+
const returnOnlySignature = createSignature(/*declaration*/ undefined, /*typeParameters*/ undefined, /*thisParameter*/ undefined, emptyArray, returnType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.IsNonInferrable);
3578735789
const returnOnlyType = createAnonymousType(node.symbol, emptySymbols, [returnOnlySignature], emptyArray, emptyArray);
3578835790
returnOnlyType.objectFlags |= ObjectFlags.NonInferrableType;
3578935791
return links.contextFreeType = returnOnlyType;

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6704,6 +6704,7 @@ export const enum SignatureFlags {
67046704
IsInnerCallChain = 1 << 3, // Indicates signature comes from a CallChain nested in an outer OptionalChain
67056705
IsOuterCallChain = 1 << 4, // Indicates signature comes from a CallChain that is the outermost chain of an optional expression
67066706
IsUntypedSignatureInJSFile = 1 << 5, // Indicates signature is from a js file and has no types
6707+
IsNonInferrable = 1 << 6, // Indicates signature comes from a non-inferrable type
67076708

67086709
// We do not propagate `IsInnerCallChain` or `IsOuterCallChain` to instantiated signatures, as that would result in us
67096710
// attempting to add `| undefined` on each recursive call to `getReturnTypeOfSignature` when
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
=== tests/cases/compiler/nonInferrableTypePropagation3.ts ===
2+
// Repro from #53748
3+
4+
declare type Callback<Args extends any[], Out, R> = (...args: Args) => (data: Out) => R;
5+
>Callback : Symbol(Callback, Decl(nonInferrableTypePropagation3.ts, 0, 0))
6+
>Args : Symbol(Args, Decl(nonInferrableTypePropagation3.ts, 2, 22))
7+
>Out : Symbol(Out, Decl(nonInferrableTypePropagation3.ts, 2, 41))
8+
>R : Symbol(R, Decl(nonInferrableTypePropagation3.ts, 2, 46))
9+
>args : Symbol(args, Decl(nonInferrableTypePropagation3.ts, 2, 53))
10+
>Args : Symbol(Args, Decl(nonInferrableTypePropagation3.ts, 2, 22))
11+
>data : Symbol(data, Decl(nonInferrableTypePropagation3.ts, 2, 72))
12+
>Out : Symbol(Out, Decl(nonInferrableTypePropagation3.ts, 2, 41))
13+
>R : Symbol(R, Decl(nonInferrableTypePropagation3.ts, 2, 46))
14+
15+
declare function factory<Out>(): <Args extends any[], R>(callback: Callback<Args, Out, R>) => (...args: Args) => R;
16+
>factory : Symbol(factory, Decl(nonInferrableTypePropagation3.ts, 2, 88))
17+
>Out : Symbol(Out, Decl(nonInferrableTypePropagation3.ts, 3, 25))
18+
>Args : Symbol(Args, Decl(nonInferrableTypePropagation3.ts, 3, 34))
19+
>R : Symbol(R, Decl(nonInferrableTypePropagation3.ts, 3, 53))
20+
>callback : Symbol(callback, Decl(nonInferrableTypePropagation3.ts, 3, 57))
21+
>Callback : Symbol(Callback, Decl(nonInferrableTypePropagation3.ts, 0, 0))
22+
>Args : Symbol(Args, Decl(nonInferrableTypePropagation3.ts, 3, 34))
23+
>Out : Symbol(Out, Decl(nonInferrableTypePropagation3.ts, 3, 25))
24+
>R : Symbol(R, Decl(nonInferrableTypePropagation3.ts, 3, 53))
25+
>args : Symbol(args, Decl(nonInferrableTypePropagation3.ts, 3, 95))
26+
>Args : Symbol(Args, Decl(nonInferrableTypePropagation3.ts, 3, 34))
27+
>R : Symbol(R, Decl(nonInferrableTypePropagation3.ts, 3, 53))
28+
29+
const make = factory<{id: string, age: number}[]>();
30+
>make : Symbol(make, Decl(nonInferrableTypePropagation3.ts, 5, 5))
31+
>factory : Symbol(factory, Decl(nonInferrableTypePropagation3.ts, 2, 88))
32+
>id : Symbol(id, Decl(nonInferrableTypePropagation3.ts, 5, 22))
33+
>age : Symbol(age, Decl(nonInferrableTypePropagation3.ts, 5, 33))
34+
35+
const usersOverAge = make((age: number) => data => {
36+
>usersOverAge : Symbol(usersOverAge, Decl(nonInferrableTypePropagation3.ts, 7, 5))
37+
>make : Symbol(make, Decl(nonInferrableTypePropagation3.ts, 5, 5))
38+
>age : Symbol(age, Decl(nonInferrableTypePropagation3.ts, 7, 27))
39+
>data : Symbol(data, Decl(nonInferrableTypePropagation3.ts, 7, 42))
40+
41+
return data.filter(user => user.age >= age);
42+
>data.filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
43+
>data : Symbol(data, Decl(nonInferrableTypePropagation3.ts, 7, 42))
44+
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
45+
>user : Symbol(user, Decl(nonInferrableTypePropagation3.ts, 8, 23))
46+
>user.age : Symbol(age, Decl(nonInferrableTypePropagation3.ts, 5, 33))
47+
>user : Symbol(user, Decl(nonInferrableTypePropagation3.ts, 8, 23))
48+
>age : Symbol(age, Decl(nonInferrableTypePropagation3.ts, 5, 33))
49+
>age : Symbol(age, Decl(nonInferrableTypePropagation3.ts, 7, 27))
50+
51+
});
52+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
=== tests/cases/compiler/nonInferrableTypePropagation3.ts ===
2+
// Repro from #53748
3+
4+
declare type Callback<Args extends any[], Out, R> = (...args: Args) => (data: Out) => R;
5+
>Callback : Callback<Args, Out, R>
6+
>args : Args
7+
>data : Out
8+
9+
declare function factory<Out>(): <Args extends any[], R>(callback: Callback<Args, Out, R>) => (...args: Args) => R;
10+
>factory : <Out>() => <Args extends any[], R>(callback: Callback<Args, Out, R>) => (...args: Args) => R
11+
>callback : Callback<Args, Out, R>
12+
>args : Args
13+
14+
const make = factory<{id: string, age: number}[]>();
15+
>make : <Args extends any[], R>(callback: Callback<Args, { id: string; age: number; }[], R>) => (...args: Args) => R
16+
>factory<{id: string, age: number}[]>() : <Args extends any[], R>(callback: Callback<Args, { id: string; age: number; }[], R>) => (...args: Args) => R
17+
>factory : <Out>() => <Args extends any[], R>(callback: Callback<Args, Out, R>) => (...args: Args) => R
18+
>id : string
19+
>age : number
20+
21+
const usersOverAge = make((age: number) => data => {
22+
>usersOverAge : (age: number) => { id: string; age: number; }[]
23+
>make((age: number) => data => { return data.filter(user => user.age >= age);}) : (age: number) => { id: string; age: number; }[]
24+
>make : <Args extends any[], R>(callback: Callback<Args, { id: string; age: number; }[], R>) => (...args: Args) => R
25+
>(age: number) => data => { return data.filter(user => user.age >= age);} : (age: number) => (data: { id: string; age: number; }[]) => { id: string; age: number; }[]
26+
>age : number
27+
>data => { return data.filter(user => user.age >= age);} : (data: { id: string; age: number; }[]) => { id: string; age: number; }[]
28+
>data : { id: string; age: number; }[]
29+
30+
return data.filter(user => user.age >= age);
31+
>data.filter(user => user.age >= age) : { id: string; age: number; }[]
32+
>data.filter : { <S extends { id: string; age: number; }>(predicate: (value: { id: string; age: number; }, index: number, array: { id: string; age: number; }[]) => value is S, thisArg?: any): S[]; (predicate: (value: { id: string; age: number; }, index: number, array: { id: string; age: number; }[]) => unknown, thisArg?: any): { id: string; age: number; }[]; }
33+
>data : { id: string; age: number; }[]
34+
>filter : { <S extends { id: string; age: number; }>(predicate: (value: { id: string; age: number; }, index: number, array: { id: string; age: number; }[]) => value is S, thisArg?: any): S[]; (predicate: (value: { id: string; age: number; }, index: number, array: { id: string; age: number; }[]) => unknown, thisArg?: any): { id: string; age: number; }[]; }
35+
>user => user.age >= age : (user: { id: string; age: number; }) => boolean
36+
>user : { id: string; age: number; }
37+
>user.age >= age : boolean
38+
>user.age : number
39+
>user : { id: string; age: number; }
40+
>age : number
41+
>age : number
42+
43+
});
44+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// Repro from #53748
5+
6+
declare type Callback<Args extends any[], Out, R> = (...args: Args) => (data: Out) => R;
7+
declare function factory<Out>(): <Args extends any[], R>(callback: Callback<Args, Out, R>) => (...args: Args) => R;
8+
9+
const make = factory<{id: string, age: number}[]>();
10+
11+
const usersOverAge = make((age: number) => data => {
12+
return data.filter(user => user.age >= age);
13+
});

0 commit comments

Comments
 (0)