Skip to content

Commit

Permalink
Properly account for type parameters introduced by contextual types (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
ahejlsberg authored Aug 8, 2024
1 parent 5f79e16 commit 278cb94
Show file tree
Hide file tree
Showing 4 changed files with 294 additions and 4 deletions.
15 changes: 11 additions & 4 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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;
}
Expand Down
90 changes: 90 additions & 0 deletions tests/baselines/reference/contextualOuterTypeParameters.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//// [tests/cases/compiler/contextualOuterTypeParameters.ts] ////

=== contextualOuterTypeParameters.ts ===
// https://github.com/microsoft/TypeScript/issues/86350

declare function f(fun: <T>(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: <T>(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: <T>(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: <T>(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))
}
};

165 changes: 165 additions & 0 deletions tests/baselines/reference/contextualOuterTypeParameters.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
//// [tests/cases/compiler/contextualOuterTypeParameters.ts] ////

=== contextualOuterTypeParameters.ts ===
// https://github.com/microsoft/TypeScript/issues/86350

declare function f(fun: <T>(t: T) => void): void
>f : (fun: <T>(t: T) => void) => void
> : ^ ^^ ^^^^^
>fun : <T>(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: T) => void) => void
> : ^ ^^ ^^^^^
>t => { type isArray = (typeof t)[] extends string[] ? true : false; type IsObject = { x: typeof t } extends { x: string } ? true : false;} : <T>(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: <T>(x: T) => void = t => {
>fn1 : <T>(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: 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: <T>(x: T) => void = function test(t) {
>fn2 : <T>(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: T) => void
> : ^ ^^ ^^^^^^^^^^^^
>test : <T>(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: <T>(x: T) => void } = {
>obj : { f: <T>(x: T) => void; }
> : ^^^^^ ^^^
>f : <T>(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: T): void; }
> : ^^^^ ^^ ^^^^^^^^^^^^^

f(t) {
>f : <T>(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
> : ^^^^^
}
};

28 changes: 28 additions & 0 deletions tests/cases/compiler/contextualOuterTypeParameters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// @strict: true
// @noemit: true

// https://github.com/microsoft/TypeScript/issues/86350

declare function f(fun: <T>(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: <T>(x: T) => void = t => {
type isArray = (typeof t)[] extends string[] ? true : false;
type IsObject = { x: typeof t } extends { x: string } ? true : false;
};

const fn2: <T>(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: <T>(x: T) => void } = {
f(t) {
type isArray = (typeof t)[] extends string[] ? true : false;
type IsObject = { x: typeof t } extends { x: string } ? true : false;
}
};

0 comments on commit 278cb94

Please sign in to comment.