Skip to content

Commit 3d8cf62

Browse files
authored
Merge pull request #16463 from Microsoft/jsdoc-@template-in-scope-as-type-parameter
Jsdoc @template in scope as type parameter
2 parents ecc2113 + 024ab09 commit 3d8cf62

File tree

5 files changed

+126
-21
lines changed

5 files changed

+126
-21
lines changed

src/compiler/checker.ts

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6170,24 +6170,16 @@ namespace ts {
61706170
return undefined;
61716171
}
61726172

6173-
function getTypeParametersFromJSDocTemplate(declaration: SignatureDeclaration): TypeParameter[] {
6174-
if (declaration.flags & NodeFlags.JavaScriptFile) {
6175-
const templateTag = getJSDocTemplateTag(declaration);
6176-
if (templateTag) {
6177-
return getTypeParametersFromDeclaration(templateTag.typeParameters);
6178-
}
6179-
}
6180-
6181-
return undefined;
6182-
}
6183-
61846173
// Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual
61856174
// type checking functions).
6186-
function getTypeParametersFromDeclaration(typeParameterDeclarations: TypeParameterDeclaration[]): TypeParameter[] {
6187-
const result: TypeParameter[] = [];
6188-
forEach(typeParameterDeclarations, node => {
6175+
function getTypeParametersFromDeclaration(declaration: DeclarationWithTypeParameters): TypeParameter[] {
6176+
let result: TypeParameter[];
6177+
forEach(getEffectiveTypeParameterDeclarations(declaration), node => {
61896178
const tp = getDeclaredTypeOfTypeParameter(node.symbol);
61906179
if (!contains(result, tp)) {
6180+
if (!result) {
6181+
result = [];
6182+
}
61916183
result.push(tp);
61926184
}
61936185
});
@@ -6383,9 +6375,7 @@ namespace ts {
63836375
const classType = declaration.kind === SyntaxKind.Constructor ?
63846376
getDeclaredTypeOfClassOrInterface(getMergedSymbol((<ClassDeclaration>declaration.parent).symbol))
63856377
: undefined;
6386-
const typeParameters = classType ? classType.localTypeParameters :
6387-
declaration.typeParameters ? getTypeParametersFromDeclaration(declaration.typeParameters) :
6388-
getTypeParametersFromJSDocTemplate(declaration);
6378+
const typeParameters = classType ? classType.localTypeParameters : getTypeParametersFromDeclaration(declaration);
63896379
const returnType = getSignatureReturnTypeFromDeclaration(declaration, isJSConstructSignature, classType);
63906380
const typePredicate = declaration.type && declaration.type.kind === SyntaxKind.TypePredicate ?
63916381
createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode) :
@@ -8166,9 +8156,9 @@ namespace ts {
81668156
case SyntaxKind.ClassExpression:
81678157
case SyntaxKind.InterfaceDeclaration:
81688158
case SyntaxKind.TypeAliasDeclaration:
8169-
const declaration = node as DeclarationWithTypeParameters;
8170-
if (declaration.typeParameters) {
8171-
for (const d of declaration.typeParameters) {
8159+
const typeParameters = getEffectiveTypeParameterDeclarations(node as DeclarationWithTypeParameters);
8160+
if (typeParameters) {
8161+
for (const d of typeParameters) {
81728162
if (contains(mappedTypes, getDeclaredTypeOfTypeParameter(getSymbolOfNode(d)))) {
81738163
return true;
81748164
}

src/compiler/utilities.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1448,7 +1448,7 @@ namespace ts {
14481448
return node && firstOrUndefined(getJSDocTags(node, kind));
14491449
}
14501450

1451-
export function getJSDocs(node: Node): (JSDoc | JSDocTag)[] {
1451+
export function getJSDocs(node: Node): (JSDoc | JSDocTag)[] {
14521452
if (isJSDocTypedefTag(node)) {
14531453
return [node.parent];
14541454
}
@@ -2743,6 +2743,20 @@ namespace ts {
27432743
}
27442744
}
27452745

2746+
/**
2747+
* Gets the effective type parameters. If the node was parsed in a
2748+
* JavaScript file, gets the type parameters from the `@template` tag from JSDoc.
2749+
*/
2750+
export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): TypeParameterDeclaration[] {
2751+
if (node.typeParameters) {
2752+
return node.typeParameters;
2753+
}
2754+
if (node.flags & NodeFlags.JavaScriptFile) {
2755+
const templateTag = getJSDocTemplateTag(node);
2756+
return templateTag && templateTag.typeParameters;
2757+
}
2758+
}
2759+
27462760
/**
27472761
* Gets the effective type annotation of the value parameter of a set accessor. If the node
27482762
* was parsed in a JavaScript file, gets the type annotation from JSDoc.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
=== tests/cases/conformance/jsdoc/jsdocTemplateTag.ts ===
2+
/**
3+
* @param {T} a
4+
* @template T
5+
*/
6+
function f<T>(a: T) {
7+
>f : Symbol(f, Decl(jsdocTemplateTag.ts, 0, 0))
8+
>T : Symbol(T, Decl(jsdocTemplateTag.ts, 4, 11))
9+
>a : Symbol(a, Decl(jsdocTemplateTag.ts, 4, 14))
10+
>T : Symbol(T, Decl(jsdocTemplateTag.ts, 4, 11))
11+
12+
return () => a
13+
>a : Symbol(a, Decl(jsdocTemplateTag.ts, 4, 14))
14+
}
15+
let n = f(1)()
16+
>n : Symbol(n, Decl(jsdocTemplateTag.ts, 7, 3))
17+
>f : Symbol(f, Decl(jsdocTemplateTag.ts, 0, 0))
18+
19+
/**
20+
* @param {T} a
21+
* @template T
22+
* @returns {function(): T}
23+
*/
24+
function g<T>(a: T) {
25+
>g : Symbol(g, Decl(jsdocTemplateTag.ts, 7, 14))
26+
>T : Symbol(T, Decl(jsdocTemplateTag.ts, 14, 11))
27+
>a : Symbol(a, Decl(jsdocTemplateTag.ts, 14, 14))
28+
>T : Symbol(T, Decl(jsdocTemplateTag.ts, 14, 11))
29+
30+
return () => a
31+
>a : Symbol(a, Decl(jsdocTemplateTag.ts, 14, 14))
32+
}
33+
let s = g('hi')()
34+
>s : Symbol(s, Decl(jsdocTemplateTag.ts, 17, 3))
35+
>g : Symbol(g, Decl(jsdocTemplateTag.ts, 7, 14))
36+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
=== tests/cases/conformance/jsdoc/jsdocTemplateTag.ts ===
2+
/**
3+
* @param {T} a
4+
* @template T
5+
*/
6+
function f<T>(a: T) {
7+
>f : <T>(a: T) => () => T
8+
>T : T
9+
>a : T
10+
>T : T
11+
12+
return () => a
13+
>() => a : () => T
14+
>a : T
15+
}
16+
let n = f(1)()
17+
>n : number
18+
>f(1)() : number
19+
>f(1) : () => number
20+
>f : <T>(a: T) => () => T
21+
>1 : 1
22+
23+
/**
24+
* @param {T} a
25+
* @template T
26+
* @returns {function(): T}
27+
*/
28+
function g<T>(a: T) {
29+
>g : <T>(a: T) => () => T
30+
>T : T
31+
>a : T
32+
>T : T
33+
34+
return () => a
35+
>() => a : () => T
36+
>a : T
37+
}
38+
let s = g('hi')()
39+
>s : string
40+
>g('hi')() : string
41+
>g('hi') : () => string
42+
>g : <T>(a: T) => () => T
43+
>'hi' : "hi"
44+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// @allowJs: true
2+
// @checkJs: true
3+
// @noEmit: true
4+
/**
5+
* @param {T} a
6+
* @template T
7+
*/
8+
function f<T>(a: T) {
9+
return () => a
10+
}
11+
let n = f(1)()
12+
13+
/**
14+
* @param {T} a
15+
* @template T
16+
* @returns {function(): T}
17+
*/
18+
function g<T>(a: T) {
19+
return () => a
20+
}
21+
let s = g('hi')()

0 commit comments

Comments
 (0)