Skip to content
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

Exclude parameters of non-inferrable signatures from inference #53756

Merged
merged 2 commits into from
Apr 14, 2023
Merged
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
16 changes: 9 additions & 7 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25009,12 +25009,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 @@ -35746,7 +35748,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);
});