Skip to content

Commit 7f3ca9f

Browse files
authored
Copy type parameters to function with @type tag (#49960)
* Copy type parameters to function with @type tag Previously, type references to generic types would fail to copy type parameters from type references in an `@type` tag. Now the type parameter is copied. Note that I left 3 places in the checker unchanged, even though they should technically also get type parameters from type refereneces: 1. getOuterTypeParameters -- not fixing this means that nested function still won't get instantiated correctly. I'll see how hard this is to fix. 2. getLocalTypeParameterOfClassOrInterfaceOrTypeAlias -- only applies to constructor functions which have a type annotation, which doesn't type check in the first place. 3. isThislessType -- This function is conservative, so should be possible to make it more conservative without writing a lot of code. Fixes #49039 * Update API baselines
1 parent aba867d commit 7f3ca9f

File tree

8 files changed

+145
-2
lines changed

8 files changed

+145
-2
lines changed

src/compiler/checker.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12749,12 +12749,14 @@ namespace ts {
1274912749

1275012750
// Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual
1275112751
// type checking functions).
12752-
function getTypeParametersFromDeclaration(declaration: DeclarationWithTypeParameters): TypeParameter[] | undefined {
12752+
function getTypeParametersFromDeclaration(declaration: DeclarationWithTypeParameters): readonly TypeParameter[] | undefined {
1275312753
let result: TypeParameter[] | undefined;
1275412754
for (const node of getEffectiveTypeParameterDeclarations(declaration)) {
1275512755
result = appendIfUnique(result, getDeclaredTypeOfTypeParameter(node.symbol));
1275612756
}
12757-
return result;
12757+
return result?.length ? result
12758+
: isFunctionDeclaration(declaration) ? getSignatureOfTypeTag(declaration)?.typeParameters
12759+
: undefined;
1275812760
}
1275912761

1276012762
function symbolsToArray(symbols: SymbolTable): Symbol[] {

src/compiler/utilitiesPublic.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,12 @@ namespace ts {
932932
/**
933933
* Gets the effective type parameters. If the node was parsed in a
934934
* JavaScript file, gets the type parameters from the `@template` tag from JSDoc.
935+
*
936+
* This does *not* return type parameters from a jsdoc reference to a generic type, eg
937+
*
938+
* type Id = <T>(x: T) => T
939+
* /** @type {Id} /
940+
* function id(x) { return x }
935941
*/
936942
export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): readonly TypeParameterDeclaration[] {
937943
if (isJSDocSignature(node)) {

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4464,6 +4464,12 @@ declare namespace ts {
44644464
/**
44654465
* Gets the effective type parameters. If the node was parsed in a
44664466
* JavaScript file, gets the type parameters from the `@template` tag from JSDoc.
4467+
*
4468+
* This does *not* return type parameters from a jsdoc reference to a generic type, eg
4469+
*
4470+
* type Id = <T>(x: T) => T
4471+
* /** @type {Id} /
4472+
* function id(x) { return x }
44674473
*/
44684474
function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): readonly TypeParameterDeclaration[];
44694475
function getEffectiveConstraintOfTypeParameter(node: TypeParameterDeclaration): TypeNode | undefined;

tests/baselines/reference/api/typescript.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4464,6 +4464,12 @@ declare namespace ts {
44644464
/**
44654465
* Gets the effective type parameters. If the node was parsed in a
44664466
* JavaScript file, gets the type parameters from the `@template` tag from JSDoc.
4467+
*
4468+
* This does *not* return type parameters from a jsdoc reference to a generic type, eg
4469+
*
4470+
* type Id = <T>(x: T) => T
4471+
* /** @type {Id} /
4472+
* function id(x) { return x }
44674473
*/
44684474
function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): readonly TypeParameterDeclaration[];
44694475
function getEffectiveConstraintOfTypeParameter(node: TypeParameterDeclaration): TypeNode | undefined;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//// [typeTagOnFunctionReferencesGeneric.js]
2+
/**
3+
* @typedef {<T>(m : T) => T} IFn
4+
*/
5+
6+
/**@type {IFn}*/
7+
export function inJs(l) {
8+
return l;
9+
}
10+
inJs(1); // lints error. Why?
11+
12+
/**@type {IFn}*/
13+
const inJsArrow = (j) => {
14+
return j;
15+
}
16+
inJsArrow(2); // no error gets linted as expected
17+
18+
19+
//// [typeTagOnFunctionReferencesGeneric.js]
20+
"use strict";
21+
/**
22+
* @typedef {<T>(m : T) => T} IFn
23+
*/
24+
exports.__esModule = true;
25+
exports.inJs = void 0;
26+
/**@type {IFn}*/
27+
function inJs(l) {
28+
return l;
29+
}
30+
exports.inJs = inJs;
31+
inJs(1); // lints error. Why?
32+
/**@type {IFn}*/
33+
var inJsArrow = function (j) {
34+
return j;
35+
};
36+
inJsArrow(2); // no error gets linted as expected
37+
38+
39+
//// [typeTagOnFunctionReferencesGeneric.d.ts]
40+
/**
41+
* @typedef {<T>(m : T) => T} IFn
42+
*/
43+
/**@type {IFn}*/
44+
export function inJs<T>(l: T): T;
45+
export type IFn = <T>(m: T) => T;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
=== tests/cases/conformance/salsa/typeTagOnFunctionReferencesGeneric.js ===
2+
/**
3+
* @typedef {<T>(m : T) => T} IFn
4+
*/
5+
6+
/**@type {IFn}*/
7+
export function inJs(l) {
8+
>inJs : Symbol(inJs, Decl(typeTagOnFunctionReferencesGeneric.js, 0, 0))
9+
>l : Symbol(l, Decl(typeTagOnFunctionReferencesGeneric.js, 5, 21))
10+
11+
return l;
12+
>l : Symbol(l, Decl(typeTagOnFunctionReferencesGeneric.js, 5, 21))
13+
}
14+
inJs(1); // lints error. Why?
15+
>inJs : Symbol(inJs, Decl(typeTagOnFunctionReferencesGeneric.js, 0, 0))
16+
17+
/**@type {IFn}*/
18+
const inJsArrow = (j) => {
19+
>inJsArrow : Symbol(inJsArrow, Decl(typeTagOnFunctionReferencesGeneric.js, 11, 5))
20+
>j : Symbol(j, Decl(typeTagOnFunctionReferencesGeneric.js, 11, 19))
21+
22+
return j;
23+
>j : Symbol(j, Decl(typeTagOnFunctionReferencesGeneric.js, 11, 19))
24+
}
25+
inJsArrow(2); // no error gets linted as expected
26+
>inJsArrow : Symbol(inJsArrow, Decl(typeTagOnFunctionReferencesGeneric.js, 11, 5))
27+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
=== tests/cases/conformance/salsa/typeTagOnFunctionReferencesGeneric.js ===
2+
/**
3+
* @typedef {<T>(m : T) => T} IFn
4+
*/
5+
6+
/**@type {IFn}*/
7+
export function inJs(l) {
8+
>inJs : <T>(l: T) => T
9+
>l : T
10+
11+
return l;
12+
>l : T
13+
}
14+
inJs(1); // lints error. Why?
15+
>inJs(1) : 1
16+
>inJs : <T>(l: T) => T
17+
>1 : 1
18+
19+
/**@type {IFn}*/
20+
const inJsArrow = (j) => {
21+
>inJsArrow : IFn
22+
>(j) => { return j;} : <T>(j: T) => T
23+
>j : T
24+
25+
return j;
26+
>j : T
27+
}
28+
inJsArrow(2); // no error gets linted as expected
29+
>inJsArrow(2) : 2
30+
>inJsArrow : IFn
31+
>2 : 2
32+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// @checkJs: true
2+
// @declaration: true
3+
// @outDir: out/
4+
// @filename: typeTagOnFunctionReferencesGeneric.js
5+
/**
6+
* @typedef {<T>(m : T) => T} IFn
7+
*/
8+
9+
/**@type {IFn}*/
10+
export function inJs(l) {
11+
return l;
12+
}
13+
inJs(1); // lints error. Why?
14+
15+
/**@type {IFn}*/
16+
const inJsArrow = (j) => {
17+
return j;
18+
}
19+
inJsArrow(2); // no error gets linted as expected

0 commit comments

Comments
 (0)