diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cb5b3abfda23a..918a7ac2c9a6e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -25026,12 +25026,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function inferFromSignature(source: Signature, target: Signature) { - const saveBivariant = bivariant; - const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown; - // Once we descend into a bivariant signature we remain bivariant for all nested inferences - bivariant = bivariant || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.MethodSignature || kind === SyntaxKind.Constructor; - applyToParameterTypes(source, target, inferFromContravariantTypesIfStrictFunctionTypes); - bivariant = saveBivariant; + if (!(source.flags & SignatureFlags.IsNonInferrable)) { + const saveBivariant = bivariant; + const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown; + // Once we descend into a bivariant signature we remain bivariant for all nested inferences + bivariant = bivariant || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.MethodSignature || kind === SyntaxKind.Constructor; + applyToParameterTypes(source, target, inferFromContravariantTypesIfStrictFunctionTypes); + bivariant = saveBivariant; + } applyToReturnTypes(source, target, inferFromTypes); } @@ -35783,7 +35785,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return links.contextFreeType; } const returnType = getReturnTypeFromBody(node, checkMode); - const returnOnlySignature = createSignature(/*declaration*/ undefined, /*typeParameters*/ undefined, /*thisParameter*/ undefined, emptyArray, returnType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None); + const returnOnlySignature = createSignature(/*declaration*/ undefined, /*typeParameters*/ undefined, /*thisParameter*/ undefined, emptyArray, returnType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.IsNonInferrable); const returnOnlyType = createAnonymousType(node.symbol, emptySymbols, [returnOnlySignature], emptyArray, emptyArray); returnOnlyType.objectFlags |= ObjectFlags.NonInferrableType; return links.contextFreeType = returnOnlyType; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b97d9becc8f68..80890b92df76b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6704,6 +6704,7 @@ export const enum SignatureFlags { IsInnerCallChain = 1 << 3, // Indicates signature comes from a CallChain nested in an outer OptionalChain IsOuterCallChain = 1 << 4, // Indicates signature comes from a CallChain that is the outermost chain of an optional expression IsUntypedSignatureInJSFile = 1 << 5, // Indicates signature is from a js file and has no types + IsNonInferrable = 1 << 6, // Indicates signature comes from a non-inferrable type // We do not propagate `IsInnerCallChain` or `IsOuterCallChain` to instantiated signatures, as that would result in us // attempting to add `| undefined` on each recursive call to `getReturnTypeOfSignature` when diff --git a/tests/baselines/reference/nonInferrableTypePropagation3.symbols b/tests/baselines/reference/nonInferrableTypePropagation3.symbols new file mode 100644 index 0000000000000..86bd7610d34c9 --- /dev/null +++ b/tests/baselines/reference/nonInferrableTypePropagation3.symbols @@ -0,0 +1,52 @@ +=== tests/cases/compiler/nonInferrableTypePropagation3.ts === +// Repro from #53748 + +declare type Callback = (...args: Args) => (data: Out) => R; +>Callback : Symbol(Callback, Decl(nonInferrableTypePropagation3.ts, 0, 0)) +>Args : Symbol(Args, Decl(nonInferrableTypePropagation3.ts, 2, 22)) +>Out : Symbol(Out, Decl(nonInferrableTypePropagation3.ts, 2, 41)) +>R : Symbol(R, Decl(nonInferrableTypePropagation3.ts, 2, 46)) +>args : Symbol(args, Decl(nonInferrableTypePropagation3.ts, 2, 53)) +>Args : Symbol(Args, Decl(nonInferrableTypePropagation3.ts, 2, 22)) +>data : Symbol(data, Decl(nonInferrableTypePropagation3.ts, 2, 72)) +>Out : Symbol(Out, Decl(nonInferrableTypePropagation3.ts, 2, 41)) +>R : Symbol(R, Decl(nonInferrableTypePropagation3.ts, 2, 46)) + +declare function factory(): (callback: Callback) => (...args: Args) => R; +>factory : Symbol(factory, Decl(nonInferrableTypePropagation3.ts, 2, 88)) +>Out : Symbol(Out, Decl(nonInferrableTypePropagation3.ts, 3, 25)) +>Args : Symbol(Args, Decl(nonInferrableTypePropagation3.ts, 3, 34)) +>R : Symbol(R, Decl(nonInferrableTypePropagation3.ts, 3, 53)) +>callback : Symbol(callback, Decl(nonInferrableTypePropagation3.ts, 3, 57)) +>Callback : Symbol(Callback, Decl(nonInferrableTypePropagation3.ts, 0, 0)) +>Args : Symbol(Args, Decl(nonInferrableTypePropagation3.ts, 3, 34)) +>Out : Symbol(Out, Decl(nonInferrableTypePropagation3.ts, 3, 25)) +>R : Symbol(R, Decl(nonInferrableTypePropagation3.ts, 3, 53)) +>args : Symbol(args, Decl(nonInferrableTypePropagation3.ts, 3, 95)) +>Args : Symbol(Args, Decl(nonInferrableTypePropagation3.ts, 3, 34)) +>R : Symbol(R, Decl(nonInferrableTypePropagation3.ts, 3, 53)) + +const make = factory<{id: string, age: number}[]>(); +>make : Symbol(make, Decl(nonInferrableTypePropagation3.ts, 5, 5)) +>factory : Symbol(factory, Decl(nonInferrableTypePropagation3.ts, 2, 88)) +>id : Symbol(id, Decl(nonInferrableTypePropagation3.ts, 5, 22)) +>age : Symbol(age, Decl(nonInferrableTypePropagation3.ts, 5, 33)) + +const usersOverAge = make((age: number) => data => { +>usersOverAge : Symbol(usersOverAge, Decl(nonInferrableTypePropagation3.ts, 7, 5)) +>make : Symbol(make, Decl(nonInferrableTypePropagation3.ts, 5, 5)) +>age : Symbol(age, Decl(nonInferrableTypePropagation3.ts, 7, 27)) +>data : Symbol(data, Decl(nonInferrableTypePropagation3.ts, 7, 42)) + + return data.filter(user => user.age >= age); +>data.filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>data : Symbol(data, Decl(nonInferrableTypePropagation3.ts, 7, 42)) +>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>user : Symbol(user, Decl(nonInferrableTypePropagation3.ts, 8, 23)) +>user.age : Symbol(age, Decl(nonInferrableTypePropagation3.ts, 5, 33)) +>user : Symbol(user, Decl(nonInferrableTypePropagation3.ts, 8, 23)) +>age : Symbol(age, Decl(nonInferrableTypePropagation3.ts, 5, 33)) +>age : Symbol(age, Decl(nonInferrableTypePropagation3.ts, 7, 27)) + +}); + diff --git a/tests/baselines/reference/nonInferrableTypePropagation3.types b/tests/baselines/reference/nonInferrableTypePropagation3.types new file mode 100644 index 0000000000000..2ba181f7cce69 --- /dev/null +++ b/tests/baselines/reference/nonInferrableTypePropagation3.types @@ -0,0 +1,44 @@ +=== tests/cases/compiler/nonInferrableTypePropagation3.ts === +// Repro from #53748 + +declare type Callback = (...args: Args) => (data: Out) => R; +>Callback : Callback +>args : Args +>data : Out + +declare function factory(): (callback: Callback) => (...args: Args) => R; +>factory : () => (callback: Callback) => (...args: Args) => R +>callback : Callback +>args : Args + +const make = factory<{id: string, age: number}[]>(); +>make : (callback: Callback) => (...args: Args) => R +>factory<{id: string, age: number}[]>() : (callback: Callback) => (...args: Args) => R +>factory : () => (callback: Callback) => (...args: Args) => R +>id : string +>age : number + +const usersOverAge = make((age: number) => data => { +>usersOverAge : (age: number) => { id: string; age: number; }[] +>make((age: number) => data => { return data.filter(user => user.age >= age);}) : (age: number) => { id: string; age: number; }[] +>make : (callback: Callback) => (...args: Args) => R +>(age: number) => data => { return data.filter(user => user.age >= age);} : (age: number) => (data: { id: string; age: number; }[]) => { id: string; age: number; }[] +>age : number +>data => { return data.filter(user => user.age >= age);} : (data: { id: string; age: number; }[]) => { id: string; age: number; }[] +>data : { id: string; age: number; }[] + + return data.filter(user => user.age >= age); +>data.filter(user => user.age >= age) : { id: string; age: number; }[] +>data.filter : { (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; }[]; } +>data : { id: string; age: number; }[] +>filter : { (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; }[]; } +>user => user.age >= age : (user: { id: string; age: number; }) => boolean +>user : { id: string; age: number; } +>user.age >= age : boolean +>user.age : number +>user : { id: string; age: number; } +>age : number +>age : number + +}); + diff --git a/tests/cases/compiler/nonInferrableTypePropagation3.ts b/tests/cases/compiler/nonInferrableTypePropagation3.ts new file mode 100644 index 0000000000000..5deec87658897 --- /dev/null +++ b/tests/cases/compiler/nonInferrableTypePropagation3.ts @@ -0,0 +1,13 @@ +// @strict: true +// @noEmit: true + +// Repro from #53748 + +declare type Callback = (...args: Args) => (data: Out) => R; +declare function factory(): (callback: Callback) => (...args: Args) => R; + +const make = factory<{id: string, age: number}[]>(); + +const usersOverAge = make((age: number) => data => { + return data.filter(user => user.age >= age); +});