Exclude parameters of non-inferrable signatures from inference (#53756)

This commit is contained in:
Anders Hejlsberg 2023-04-14 12:44:37 -07:00 committed by GitHub
parent b92483f20c
commit e49db97f97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 119 additions and 7 deletions

View File

@ -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;

View File

@ -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

View File

@ -0,0 +1,52 @@
=== tests/cases/compiler/nonInferrableTypePropagation3.ts ===
// Repro from #53748
declare type Callback<Args extends any[], Out, R> = (...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<Out>(): <Args extends any[], R>(callback: Callback<Args, Out, R>) => (...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))
});

View File

@ -0,0 +1,44 @@
=== tests/cases/compiler/nonInferrableTypePropagation3.ts ===
// Repro from #53748
declare type Callback<Args extends any[], Out, R> = (...args: Args) => (data: Out) => R;
>Callback : Callback<Args, Out, R>
>args : Args
>data : Out
declare function factory<Out>(): <Args extends any[], R>(callback: Callback<Args, Out, R>) => (...args: Args) => R;
>factory : <Out>() => <Args extends any[], R>(callback: Callback<Args, Out, R>) => (...args: Args) => R
>callback : Callback<Args, Out, R>
>args : Args
const make = factory<{id: string, age: number}[]>();
>make : <Args extends any[], R>(callback: Callback<Args, { id: string; age: number; }[], R>) => (...args: Args) => R
>factory<{id: string, age: number}[]>() : <Args extends any[], R>(callback: Callback<Args, { id: string; age: number; }[], R>) => (...args: Args) => R
>factory : <Out>() => <Args extends any[], R>(callback: Callback<Args, Out, R>) => (...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 : <Args extends any[], R>(callback: Callback<Args, { id: string; age: number; }[], R>) => (...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 : { <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; }[]; }
>data : { id: string; age: number; }[]
>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; }[]; }
>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
});

View File

@ -0,0 +1,13 @@
// @strict: true
// @noEmit: true
// Repro from #53748
declare type Callback<Args extends any[], Out, R> = (...args: Args) => (data: Out) => R;
declare function factory<Out>(): <Args extends any[], R>(callback: Callback<Args, Out, R>) => (...args: Args) => R;
const make = factory<{id: string, age: number}[]>();
const usersOverAge = make((age: number) => data => {
return data.filter(user => user.age >= age);
});