Skip to content

Commit

Permalink
Exclude parameters of non-inferrable signatures from inference (#53756)
Browse files Browse the repository at this point in the history
  • Loading branch information
ahejlsberg authored Apr 14, 2023
1 parent b92483f commit e49db97
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 7 deletions.
16 changes: 9 additions & 7 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
52 changes: 52 additions & 0 deletions tests/baselines/reference/nonInferrableTypePropagation3.symbols
Original file line number Diff line number Diff line change
@@ -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))

});

44 changes: 44 additions & 0 deletions tests/baselines/reference/nonInferrableTypePropagation3.types
Original file line number Diff line number Diff line change
@@ -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

});

13 changes: 13 additions & 0 deletions tests/cases/compiler/nonInferrableTypePropagation3.ts
Original file line number Diff line number Diff line change
@@ -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);
});

0 comments on commit e49db97

Please sign in to comment.