From 278cb9489dbc8011c05573f26c33f46a005d3955 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 8 Aug 2024 13:38:46 -0700 Subject: [PATCH] Properly account for type parameters introduced by contextual types (#59516) --- src/compiler/checker.ts | 15 +- .../contextualOuterTypeParameters.symbols | 90 ++++++++++ .../contextualOuterTypeParameters.types | 165 ++++++++++++++++++ .../compiler/contextualOuterTypeParameters.ts | 28 +++ 4 files changed, 294 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/contextualOuterTypeParameters.symbols create mode 100644 tests/baselines/reference/contextualOuterTypeParameters.types create mode 100644 tests/cases/compiler/contextualOuterTypeParameters.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3c6713a8262..9ca49d3cdee 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12573,7 +12573,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (!node) { return undefined; } - switch (node.kind) { + const kind = node.kind; + switch (kind) { case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: case SyntaxKind.InterfaceDeclaration: @@ -12595,15 +12596,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.MappedType: case SyntaxKind.ConditionalType: { const outerTypeParameters = getOuterTypeParameters(node, includeThisTypes); - if (node.kind === SyntaxKind.MappedType) { + if ((kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction || isObjectLiteralMethod(node)) && isContextSensitive(node as Expression | MethodDeclaration)) { + const signature = firstOrUndefined(getSignaturesOfType(getTypeOfSymbol(getSymbolOfDeclaration(node as FunctionLikeDeclaration)), SignatureKind.Call)); + if (signature && signature.typeParameters) { + return [...(outerTypeParameters || emptyArray), ...signature.typeParameters]; + } + } + if (kind === SyntaxKind.MappedType) { return append(outerTypeParameters, getDeclaredTypeOfTypeParameter(getSymbolOfDeclaration((node as MappedTypeNode).typeParameter))); } - else if (node.kind === SyntaxKind.ConditionalType) { + else if (kind === SyntaxKind.ConditionalType) { return concatenate(outerTypeParameters, getInferTypeParameters(node as ConditionalTypeNode)); } const outerAndOwnTypeParameters = appendTypeParameters(outerTypeParameters, getEffectiveTypeParameterDeclarations(node as DeclarationWithTypeParameters)); const thisType = includeThisTypes && - (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.InterfaceDeclaration || isJSConstructor(node)) && + (kind === SyntaxKind.ClassDeclaration || kind === SyntaxKind.ClassExpression || kind === SyntaxKind.InterfaceDeclaration || isJSConstructor(node)) && getDeclaredTypeOfClassOrInterface(getSymbolOfDeclaration(node as ClassLikeDeclaration | InterfaceDeclaration)).thisType; return thisType ? append(outerAndOwnTypeParameters, thisType) : outerAndOwnTypeParameters; } diff --git a/tests/baselines/reference/contextualOuterTypeParameters.symbols b/tests/baselines/reference/contextualOuterTypeParameters.symbols new file mode 100644 index 00000000000..72ec61e99b1 --- /dev/null +++ b/tests/baselines/reference/contextualOuterTypeParameters.symbols @@ -0,0 +1,90 @@ +//// [tests/cases/compiler/contextualOuterTypeParameters.ts] //// + +=== contextualOuterTypeParameters.ts === +// https://github.com/microsoft/TypeScript/issues/59450 + +declare function f(fun: (t: T) => void): void +>f : Symbol(f, Decl(contextualOuterTypeParameters.ts, 0, 0)) +>fun : Symbol(fun, Decl(contextualOuterTypeParameters.ts, 2, 19)) +>T : Symbol(T, Decl(contextualOuterTypeParameters.ts, 2, 25)) +>t : Symbol(t, Decl(contextualOuterTypeParameters.ts, 2, 28)) +>T : Symbol(T, Decl(contextualOuterTypeParameters.ts, 2, 25)) + +f(t => { +>f : Symbol(f, Decl(contextualOuterTypeParameters.ts, 0, 0)) +>t : Symbol(t, Decl(contextualOuterTypeParameters.ts, 4, 2)) + + type isArray = (typeof t)[] extends string[] ? true : false; +>isArray : Symbol(isArray, Decl(contextualOuterTypeParameters.ts, 4, 8)) +>t : Symbol(t, Decl(contextualOuterTypeParameters.ts, 4, 2)) + + type IsObject = { x: typeof t } extends { x: string } ? true : false; +>IsObject : Symbol(IsObject, Decl(contextualOuterTypeParameters.ts, 5, 64)) +>x : Symbol(x, Decl(contextualOuterTypeParameters.ts, 6, 21)) +>t : Symbol(t, Decl(contextualOuterTypeParameters.ts, 4, 2)) +>x : Symbol(x, Decl(contextualOuterTypeParameters.ts, 6, 45)) + +}); + +const fn1: (x: T) => void = t => { +>fn1 : Symbol(fn1, Decl(contextualOuterTypeParameters.ts, 9, 5)) +>T : Symbol(T, Decl(contextualOuterTypeParameters.ts, 9, 12)) +>x : Symbol(x, Decl(contextualOuterTypeParameters.ts, 9, 15)) +>T : Symbol(T, Decl(contextualOuterTypeParameters.ts, 9, 12)) +>t : Symbol(t, Decl(contextualOuterTypeParameters.ts, 9, 30)) + + type isArray = (typeof t)[] extends string[] ? true : false; +>isArray : Symbol(isArray, Decl(contextualOuterTypeParameters.ts, 9, 37)) +>t : Symbol(t, Decl(contextualOuterTypeParameters.ts, 9, 30)) + + type IsObject = { x: typeof t } extends { x: string } ? true : false; +>IsObject : Symbol(IsObject, Decl(contextualOuterTypeParameters.ts, 10, 64)) +>x : Symbol(x, Decl(contextualOuterTypeParameters.ts, 11, 21)) +>t : Symbol(t, Decl(contextualOuterTypeParameters.ts, 9, 30)) +>x : Symbol(x, Decl(contextualOuterTypeParameters.ts, 11, 45)) + +}; + +const fn2: (x: T) => void = function test(t) { +>fn2 : Symbol(fn2, Decl(contextualOuterTypeParameters.ts, 14, 5)) +>T : Symbol(T, Decl(contextualOuterTypeParameters.ts, 14, 12)) +>x : Symbol(x, Decl(contextualOuterTypeParameters.ts, 14, 15)) +>T : Symbol(T, Decl(contextualOuterTypeParameters.ts, 14, 12)) +>test : Symbol(test, Decl(contextualOuterTypeParameters.ts, 14, 30)) +>t : Symbol(t, Decl(contextualOuterTypeParameters.ts, 14, 45)) + + type isArray = (typeof t)[] extends string[] ? true : false; +>isArray : Symbol(isArray, Decl(contextualOuterTypeParameters.ts, 14, 49)) +>t : Symbol(t, Decl(contextualOuterTypeParameters.ts, 14, 45)) + + type IsObject = { x: typeof t } extends { x: string } ? true : false; +>IsObject : Symbol(IsObject, Decl(contextualOuterTypeParameters.ts, 15, 64)) +>x : Symbol(x, Decl(contextualOuterTypeParameters.ts, 16, 21)) +>t : Symbol(t, Decl(contextualOuterTypeParameters.ts, 14, 45)) +>x : Symbol(x, Decl(contextualOuterTypeParameters.ts, 16, 45)) + +}; + +const obj: { f: (x: T) => void } = { +>obj : Symbol(obj, Decl(contextualOuterTypeParameters.ts, 19, 5)) +>f : Symbol(f, Decl(contextualOuterTypeParameters.ts, 19, 12)) +>T : Symbol(T, Decl(contextualOuterTypeParameters.ts, 19, 17)) +>x : Symbol(x, Decl(contextualOuterTypeParameters.ts, 19, 20)) +>T : Symbol(T, Decl(contextualOuterTypeParameters.ts, 19, 17)) + + f(t) { +>f : Symbol(f, Decl(contextualOuterTypeParameters.ts, 19, 39)) +>t : Symbol(t, Decl(contextualOuterTypeParameters.ts, 20, 6)) + + type isArray = (typeof t)[] extends string[] ? true : false; +>isArray : Symbol(isArray, Decl(contextualOuterTypeParameters.ts, 20, 10)) +>t : Symbol(t, Decl(contextualOuterTypeParameters.ts, 20, 6)) + + type IsObject = { x: typeof t } extends { x: string } ? true : false; +>IsObject : Symbol(IsObject, Decl(contextualOuterTypeParameters.ts, 21, 68)) +>x : Symbol(x, Decl(contextualOuterTypeParameters.ts, 22, 25)) +>t : Symbol(t, Decl(contextualOuterTypeParameters.ts, 20, 6)) +>x : Symbol(x, Decl(contextualOuterTypeParameters.ts, 22, 49)) + } +}; + diff --git a/tests/baselines/reference/contextualOuterTypeParameters.types b/tests/baselines/reference/contextualOuterTypeParameters.types new file mode 100644 index 00000000000..d18525d58c1 --- /dev/null +++ b/tests/baselines/reference/contextualOuterTypeParameters.types @@ -0,0 +1,165 @@ +//// [tests/cases/compiler/contextualOuterTypeParameters.ts] //// + +=== contextualOuterTypeParameters.ts === +// https://github.com/microsoft/TypeScript/issues/59450 + +declare function f(fun: (t: T) => void): void +>f : (fun: (t: T) => void) => void +> : ^ ^^ ^^^^^ +>fun : (t: T) => void +> : ^ ^^ ^^ ^^^^^ +>t : T +> : ^ + +f(t => { +>f(t => { type isArray = (typeof t)[] extends string[] ? true : false; type IsObject = { x: typeof t } extends { x: string } ? true : false;}) : void +> : ^^^^ +>f : (fun: (t: T) => void) => void +> : ^ ^^ ^^^^^ +>t => { type isArray = (typeof t)[] extends string[] ? true : false; type IsObject = { x: typeof t } extends { x: string } ? true : false;} : (t: T) => void +> : ^ ^^ ^^^^^^^^^^^^ +>t : T +> : ^ + + type isArray = (typeof t)[] extends string[] ? true : false; +>isArray : T[] extends string[] ? true : false +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>t : T +> : ^ +>true : true +> : ^^^^ +>false : false +> : ^^^^^ + + type IsObject = { x: typeof t } extends { x: string } ? true : false; +>IsObject : { x: typeof t; } extends { x: string; } ? true : false +> : ^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ +>x : T +> : ^ +>t : T +> : ^ +>x : string +> : ^^^^^^ +>true : true +> : ^^^^ +>false : false +> : ^^^^^ + +}); + +const fn1: (x: T) => void = t => { +>fn1 : (x: T) => void +> : ^ ^^ ^^ ^^^^^ +>x : T +> : ^ +>t => { type isArray = (typeof t)[] extends string[] ? true : false; type IsObject = { x: typeof t } extends { x: string } ? true : false;} : (t: T) => void +> : ^ ^^ ^^^^^^^^^^^^ +>t : T +> : ^ + + type isArray = (typeof t)[] extends string[] ? true : false; +>isArray : T[] extends string[] ? true : false +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>t : T +> : ^ +>true : true +> : ^^^^ +>false : false +> : ^^^^^ + + type IsObject = { x: typeof t } extends { x: string } ? true : false; +>IsObject : { x: typeof t; } extends { x: string; } ? true : false +> : ^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ +>x : T +> : ^ +>t : T +> : ^ +>x : string +> : ^^^^^^ +>true : true +> : ^^^^ +>false : false +> : ^^^^^ + +}; + +const fn2: (x: T) => void = function test(t) { +>fn2 : (x: T) => void +> : ^ ^^ ^^ ^^^^^ +>x : T +> : ^ +>function test(t) { type isArray = (typeof t)[] extends string[] ? true : false; type IsObject = { x: typeof t } extends { x: string } ? true : false;} : (t: T) => void +> : ^ ^^ ^^^^^^^^^^^^ +>test : (t: T) => void +> : ^ ^^ ^^^^^^^^^^^^ +>t : T +> : ^ + + type isArray = (typeof t)[] extends string[] ? true : false; +>isArray : T[] extends string[] ? true : false +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>t : T +> : ^ +>true : true +> : ^^^^ +>false : false +> : ^^^^^ + + type IsObject = { x: typeof t } extends { x: string } ? true : false; +>IsObject : { x: typeof t; } extends { x: string; } ? true : false +> : ^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ +>x : T +> : ^ +>t : T +> : ^ +>x : string +> : ^^^^^^ +>true : true +> : ^^^^ +>false : false +> : ^^^^^ + +}; + +const obj: { f: (x: T) => void } = { +>obj : { f: (x: T) => void; } +> : ^^^^^ ^^^ +>f : (x: T) => void +> : ^ ^^ ^^ ^^^^^ +>x : T +> : ^ +>{ f(t) { type isArray = (typeof t)[] extends string[] ? true : false; type IsObject = { x: typeof t } extends { x: string } ? true : false; }} : { f(t: T): void; } +> : ^^^^ ^^ ^^^^^^^^^^^^^ + + f(t) { +>f : (t: T) => void +> : ^ ^^ ^^^^^^^^^^^^ +>t : T +> : ^ + + type isArray = (typeof t)[] extends string[] ? true : false; +>isArray : T[] extends string[] ? true : false +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>t : T +> : ^ +>true : true +> : ^^^^ +>false : false +> : ^^^^^ + + type IsObject = { x: typeof t } extends { x: string } ? true : false; +>IsObject : { x: typeof t; } extends { x: string; } ? true : false +> : ^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ +>x : T +> : ^ +>t : T +> : ^ +>x : string +> : ^^^^^^ +>true : true +> : ^^^^ +>false : false +> : ^^^^^ + } +}; + diff --git a/tests/cases/compiler/contextualOuterTypeParameters.ts b/tests/cases/compiler/contextualOuterTypeParameters.ts new file mode 100644 index 00000000000..3ed24f2a3c7 --- /dev/null +++ b/tests/cases/compiler/contextualOuterTypeParameters.ts @@ -0,0 +1,28 @@ +// @strict: true +// @noemit: true + +// https://github.com/microsoft/TypeScript/issues/59450 + +declare function f(fun: (t: T) => void): void + +f(t => { + type isArray = (typeof t)[] extends string[] ? true : false; + type IsObject = { x: typeof t } extends { x: string } ? true : false; +}); + +const fn1: (x: T) => void = t => { + type isArray = (typeof t)[] extends string[] ? true : false; + type IsObject = { x: typeof t } extends { x: string } ? true : false; +}; + +const fn2: (x: T) => void = function test(t) { + type isArray = (typeof t)[] extends string[] ? true : false; + type IsObject = { x: typeof t } extends { x: string } ? true : false; +}; + +const obj: { f: (x: T) => void } = { + f(t) { + type isArray = (typeof t)[] extends string[] ? true : false; + type IsObject = { x: typeof t } extends { x: string } ? true : false; + } +};