Skip to content

Commit 3b107fe

Browse files
authored
Fix @param type parameter lookup (microsoft#39532)
Previously, getObjectTypeInstantiation had special-case code to look up type parameters for `@param` as if they were in the parameter location. This should occur in the main lookup loop of `getOuterTypeParameters`, however. The current code only runs once, which is not sufficient, and it also jumps to the parameter for any type contained in a `@param`, which skips type parameters that occur in the tag itself.
1 parent b397d1f commit 3b107fe

File tree

4 files changed

+69
-12
lines changed

4 files changed

+69
-12
lines changed

src/compiler/checker.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8758,6 +8758,12 @@ namespace ts {
87588758
(node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.InterfaceDeclaration || isJSConstructor(node)) &&
87598759
getDeclaredTypeOfClassOrInterface(getSymbolOfNode(node as ClassLikeDeclaration | InterfaceDeclaration)).thisType;
87608760
return thisType ? append(outerAndOwnTypeParameters, thisType) : outerAndOwnTypeParameters;
8761+
case SyntaxKind.JSDocParameterTag:
8762+
const paramSymbol = getParameterSymbolFromJSDoc(node as JSDocParameterTag);
8763+
if (paramSymbol) {
8764+
node = paramSymbol.valueDeclaration;
8765+
}
8766+
break;
87618767
}
87628768
}
87638769
}
@@ -14584,24 +14590,14 @@ namespace ts {
1458414590

1458514591
function getObjectTypeInstantiation(type: AnonymousType | DeferredTypeReference, mapper: TypeMapper) {
1458614592
const target = type.objectFlags & ObjectFlags.Instantiated ? type.target! : type;
14587-
const node = type.objectFlags & ObjectFlags.Reference ? (<TypeReference>type).node! : type.symbol.declarations[0];
14588-
const links = getNodeLinks(node);
14593+
const declaration = type.objectFlags & ObjectFlags.Reference ? (<TypeReference>type).node! : type.symbol.declarations[0];
14594+
const links = getNodeLinks(declaration);
1458914595
let typeParameters = links.outerTypeParameters;
1459014596
if (!typeParameters) {
1459114597
// The first time an anonymous type is instantiated we compute and store a list of the type
1459214598
// parameters that are in scope (and therefore potentially referenced). For type literals that
1459314599
// aren't the right hand side of a generic type alias declaration we optimize by reducing the
1459414600
// set of type parameters to those that are possibly referenced in the literal.
14595-
let declaration = node;
14596-
if (isInJSFile(declaration)) {
14597-
const paramTag = findAncestor(declaration, isJSDocParameterTag);
14598-
if (paramTag) {
14599-
const paramSymbol = getParameterSymbolFromJSDoc(paramTag);
14600-
if (paramSymbol) {
14601-
declaration = paramSymbol.valueDeclaration;
14602-
}
14603-
}
14604-
}
1460514601
let outerTypeParameters = getOuterTypeParameters(declaration, /*includeThisTypes*/ true);
1460614602
if (isJSConstructor(declaration)) {
1460714603
const templateTagParameters = getTypeParametersFromDeclaration(declaration as DeclarationWithTypeParameters);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
=== tests/cases/conformance/jsdoc/38572.js ===
2+
/**
3+
* @template T
4+
* @param {T} a
5+
* @param {{[K in keyof T]: (value: T[K]) => void }} b
6+
*/
7+
function f(a, b) {
8+
>f : Symbol(f, Decl(38572.js, 0, 0))
9+
>a : Symbol(a, Decl(38572.js, 5, 11))
10+
>b : Symbol(b, Decl(38572.js, 5, 13))
11+
}
12+
13+
f({ x: 42 }, { x(param) { param.toFixed() } });
14+
>f : Symbol(f, Decl(38572.js, 0, 0))
15+
>x : Symbol(x, Decl(38572.js, 8, 3))
16+
>x : Symbol(x, Decl(38572.js, 8, 14))
17+
>param : Symbol(param, Decl(38572.js, 8, 17))
18+
>param.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --))
19+
>param : Symbol(param, Decl(38572.js, 8, 17))
20+
>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --))
21+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
=== tests/cases/conformance/jsdoc/38572.js ===
2+
/**
3+
* @template T
4+
* @param {T} a
5+
* @param {{[K in keyof T]: (value: T[K]) => void }} b
6+
*/
7+
function f(a, b) {
8+
>f : <T>(a: T, b: { [K in keyof T]: (value: T[K]) => void; }) => void
9+
>a : T
10+
>b : { [K in keyof T]: (value: T[K]) => void; }
11+
}
12+
13+
f({ x: 42 }, { x(param) { param.toFixed() } });
14+
>f({ x: 42 }, { x(param) { param.toFixed() } }) : void
15+
>f : <T>(a: T, b: { [K in keyof T]: (value: T[K]) => void; }) => void
16+
>{ x: 42 } : { x: number; }
17+
>x : number
18+
>42 : 42
19+
>{ x(param) { param.toFixed() } } : { x(param: number): void; }
20+
>x : (param: number) => void
21+
>param : number
22+
>param.toFixed() : string
23+
>param.toFixed : (fractionDigits?: number) => string
24+
>param : number
25+
>toFixed : (fractionDigits?: number) => string
26+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// @noEmit: true
2+
// @allowJs: true
3+
// @checkJs: true
4+
// @Filename: 38572.js
5+
6+
/**
7+
* @template T
8+
* @param {T} a
9+
* @param {{[K in keyof T]: (value: T[K]) => void }} b
10+
*/
11+
function f(a, b) {
12+
}
13+
14+
f({ x: 42 }, { x(param) { param.toFixed() } });

0 commit comments

Comments
 (0)