Skip to content

Commit a36d04f

Browse files
authored
feat(56634): Support for "const modifier on type parameters" with JSDoc (microsoft#56649)
1 parent 2c134db commit a36d04f

13 files changed

+1071
-6
lines changed

src/compiler/checker.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -48798,18 +48798,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4879848798
}
4879948799
}
4880048800
switch (modifier.kind) {
48801-
case SyntaxKind.ConstKeyword:
48801+
case SyntaxKind.ConstKeyword: {
4880248802
if (node.kind !== SyntaxKind.EnumDeclaration && node.kind !== SyntaxKind.TypeParameter) {
4880348803
return grammarErrorOnNode(node, Diagnostics.A_class_member_cannot_have_the_0_keyword, tokenToString(SyntaxKind.ConstKeyword));
4880448804
}
48805-
const parent = node.parent;
48805+
const parent = (isJSDocTemplateTag(node.parent) && getEffectiveJSDocHost(node.parent)) || node.parent;
4880648806
if (
4880748807
node.kind === SyntaxKind.TypeParameter && !(isFunctionLikeDeclaration(parent) || isClassLike(parent) || isFunctionTypeNode(parent) ||
4880848808
isConstructorTypeNode(parent) || isCallSignatureDeclaration(parent) || isConstructSignatureDeclaration(parent) || isMethodSignature(parent))
4880948809
) {
4881048810
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_can_only_appear_on_a_type_parameter_of_a_function_method_or_class, tokenToString(modifier.kind));
4881148811
}
4881248812
break;
48813+
}
4881348814
case SyntaxKind.OverrideKeyword:
4881448815
// If node.kind === SyntaxKind.Parameter, checkParameter reports an error if it's not a parameter property.
4881548816
if (flags & ModifierFlags.Override) {
@@ -49088,10 +49089,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4908849089
break;
4908949090

4909049091
case SyntaxKind.InKeyword:
49091-
case SyntaxKind.OutKeyword:
49092+
case SyntaxKind.OutKeyword: {
4909249093
const inOutFlag = modifier.kind === SyntaxKind.InKeyword ? ModifierFlags.In : ModifierFlags.Out;
4909349094
const inOutText = modifier.kind === SyntaxKind.InKeyword ? "in" : "out";
49094-
if (node.kind !== SyntaxKind.TypeParameter || !(isInterfaceDeclaration(node.parent) || isClassLike(node.parent) || isTypeAliasDeclaration(node.parent))) {
49095+
const parent = isJSDocTemplateTag(node.parent) && (getEffectiveJSDocHost(node.parent) || find(getJSDocRoot(node.parent)?.tags, isJSDocTypedefTag)) || node.parent;
49096+
if (node.kind !== SyntaxKind.TypeParameter || parent && !(isInterfaceDeclaration(parent) || isClassLike(parent) || isTypeAliasDeclaration(parent) || isJSDocTypedefTag(parent))) {
4909549097
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_can_only_appear_on_a_type_parameter_of_a_class_interface_or_type_alias, inOutText);
4909649098
}
4909749099
if (flags & inOutFlag) {
@@ -49102,6 +49104,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4910249104
}
4910349105
flags |= inOutFlag;
4910449106
break;
49107+
}
4910549108
}
4910649109
}
4910749110
}

src/compiler/parser.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -9710,8 +9710,9 @@ namespace Parser {
97109710
if (isBracketed) {
97119711
skipWhitespace();
97129712
}
9713-
const name = parseJSDocIdentifierName(Diagnostics.Unexpected_token_A_type_parameter_name_was_expected_without_curly_braces);
97149713

9714+
const modifiers = parseModifiers(/*allowDecorators*/ false, /*permitConstAsModifier*/ true);
9715+
const name = parseJSDocIdentifierName(Diagnostics.Unexpected_token_A_type_parameter_name_was_expected_without_curly_braces);
97159716
let defaultType: TypeNode | undefined;
97169717
if (isBracketed) {
97179718
skipWhitespace();
@@ -9723,7 +9724,7 @@ namespace Parser {
97239724
if (nodeIsMissing(name)) {
97249725
return undefined;
97259726
}
9726-
return finishNode(factory.createTypeParameterDeclaration(/*modifiers*/ undefined, name, /*constraint*/ undefined, defaultType), typeParameterPos);
9727+
return finishNode(factory.createTypeParameterDeclaration(modifiers, name, /*constraint*/ undefined, defaultType), typeParameterPos);
97279728
}
97289729

97299730
function parseTemplateTagTypeParameters() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
//// [tests/cases/conformance/jsdoc/jsdocTemplateTag6.ts] ////
2+
3+
=== a.js ===
4+
/**
5+
* @template const T
6+
* @param {T} x
7+
* @returns {T}
8+
*/
9+
function f1(x) {
10+
>f1 : Symbol(f1, Decl(a.js, 0, 0))
11+
>x : Symbol(x, Decl(a.js, 5, 12))
12+
13+
return x;
14+
>x : Symbol(x, Decl(a.js, 5, 12))
15+
}
16+
const t1 = f1("a");
17+
>t1 : Symbol(t1, Decl(a.js, 8, 5))
18+
>f1 : Symbol(f1, Decl(a.js, 0, 0))
19+
20+
const t2 = f1(["a", ["b", "c"]]);
21+
>t2 : Symbol(t2, Decl(a.js, 9, 5))
22+
>f1 : Symbol(f1, Decl(a.js, 0, 0))
23+
24+
const t3 = f1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] });
25+
>t3 : Symbol(t3, Decl(a.js, 10, 5))
26+
>f1 : Symbol(f1, Decl(a.js, 0, 0))
27+
>a : Symbol(a, Decl(a.js, 10, 15))
28+
>b : Symbol(b, Decl(a.js, 10, 21))
29+
>d : Symbol(d, Decl(a.js, 10, 29))
30+
>f : Symbol(f, Decl(a.js, 10, 49))
31+
32+
/**
33+
* @template const T, U
34+
* @param {T} x
35+
* @returns {T}
36+
*/
37+
function f2(x) {
38+
>f2 : Symbol(f2, Decl(a.js, 10, 63))
39+
>x : Symbol(x, Decl(a.js, 17, 12))
40+
41+
return x;
42+
>x : Symbol(x, Decl(a.js, 17, 12))
43+
44+
};
45+
const t4 = f2('a');
46+
>t4 : Symbol(t4, Decl(a.js, 20, 5))
47+
>f2 : Symbol(f2, Decl(a.js, 10, 63))
48+
49+
const t5 = f2(['a', ['b', 'c']]);
50+
>t5 : Symbol(t5, Decl(a.js, 21, 5))
51+
>f2 : Symbol(f2, Decl(a.js, 10, 63))
52+
53+
const t6 = f2({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] });
54+
>t6 : Symbol(t6, Decl(a.js, 22, 5))
55+
>f2 : Symbol(f2, Decl(a.js, 10, 63))
56+
>a : Symbol(a, Decl(a.js, 22, 15))
57+
>b : Symbol(b, Decl(a.js, 22, 21))
58+
>d : Symbol(d, Decl(a.js, 22, 29))
59+
>f : Symbol(f, Decl(a.js, 22, 49))
60+
61+
/**
62+
* @template const T
63+
* @param {T} x
64+
* @returns {T[]}
65+
*/
66+
function f3(x) {
67+
>f3 : Symbol(f3, Decl(a.js, 22, 63))
68+
>x : Symbol(x, Decl(a.js, 29, 12))
69+
70+
return [x];
71+
>x : Symbol(x, Decl(a.js, 29, 12))
72+
}
73+
const t7 = f3("hello");
74+
>t7 : Symbol(t7, Decl(a.js, 32, 5))
75+
>f3 : Symbol(f3, Decl(a.js, 22, 63))
76+
77+
const t8 = f3("hello");
78+
>t8 : Symbol(t8, Decl(a.js, 33, 5))
79+
>f3 : Symbol(f3, Decl(a.js, 22, 63))
80+
81+
/**
82+
* @template const T
83+
* @param {[T, T]} x
84+
* @returns {T}
85+
*/
86+
function f4(x) {
87+
>f4 : Symbol(f4, Decl(a.js, 33, 23))
88+
>x : Symbol(x, Decl(a.js, 40, 12))
89+
90+
return x[0];
91+
>x : Symbol(x, Decl(a.js, 40, 12))
92+
>0 : Symbol(0)
93+
}
94+
const t9 = f4([[1, "x"], [2, "y"]]);
95+
>t9 : Symbol(t9, Decl(a.js, 43, 5))
96+
>f4 : Symbol(f4, Decl(a.js, 33, 23))
97+
98+
const t10 = f4([{ a: 1, b: "x" }, { a: 2, b: "y" }]);
99+
>t10 : Symbol(t10, Decl(a.js, 44, 5))
100+
>f4 : Symbol(f4, Decl(a.js, 33, 23))
101+
>a : Symbol(a, Decl(a.js, 44, 17))
102+
>b : Symbol(b, Decl(a.js, 44, 23))
103+
>a : Symbol(a, Decl(a.js, 44, 35))
104+
>b : Symbol(b, Decl(a.js, 44, 41))
105+
106+
/**
107+
* @template const T
108+
* @param {{ x: T, y: T}} obj
109+
* @returns {T}
110+
*/
111+
function f5(obj) {
112+
>f5 : Symbol(f5, Decl(a.js, 44, 53))
113+
>obj : Symbol(obj, Decl(a.js, 51, 12))
114+
115+
return obj.x;
116+
>obj.x : Symbol(x, Decl(a.js, 48, 12))
117+
>obj : Symbol(obj, Decl(a.js, 51, 12))
118+
>x : Symbol(x, Decl(a.js, 48, 12))
119+
}
120+
const t11 = f5({ x: [1, "x"], y: [2, "y"] });
121+
>t11 : Symbol(t11, Decl(a.js, 54, 5))
122+
>f5 : Symbol(f5, Decl(a.js, 44, 53))
123+
>x : Symbol(x, Decl(a.js, 54, 16))
124+
>y : Symbol(y, Decl(a.js, 54, 29))
125+
126+
const t12 = f5({ x: { a: 1, b: "x" }, y: { a: 2, b: "y" } });
127+
>t12 : Symbol(t12, Decl(a.js, 55, 5))
128+
>f5 : Symbol(f5, Decl(a.js, 44, 53))
129+
>x : Symbol(x, Decl(a.js, 55, 16))
130+
>a : Symbol(a, Decl(a.js, 55, 21))
131+
>b : Symbol(b, Decl(a.js, 55, 27))
132+
>y : Symbol(y, Decl(a.js, 55, 37))
133+
>a : Symbol(a, Decl(a.js, 55, 42))
134+
>b : Symbol(b, Decl(a.js, 55, 48))
135+
136+
/**
137+
* @template const T
138+
*/
139+
class C {
140+
>C : Symbol(C, Decl(a.js, 55, 61))
141+
142+
/**
143+
* @param {T} x
144+
*/
145+
constructor(x) {}
146+
>x : Symbol(x, Decl(a.js, 64, 16))
147+
148+
/**
149+
* @template const U
150+
* @param {U} x
151+
*/
152+
foo(x) {
153+
>foo : Symbol(C.foo, Decl(a.js, 64, 21))
154+
>x : Symbol(x, Decl(a.js, 70, 8))
155+
156+
return x;
157+
>x : Symbol(x, Decl(a.js, 70, 8))
158+
}
159+
}
160+
161+
const t13 = new C({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] });
162+
>t13 : Symbol(t13, Decl(a.js, 75, 5))
163+
>C : Symbol(C, Decl(a.js, 55, 61))
164+
>a : Symbol(a, Decl(a.js, 75, 19))
165+
>b : Symbol(b, Decl(a.js, 75, 25))
166+
>d : Symbol(d, Decl(a.js, 75, 33))
167+
>f : Symbol(f, Decl(a.js, 75, 53))
168+
169+
const t14 = t13.foo(["a", ["b", "c"]]);
170+
>t14 : Symbol(t14, Decl(a.js, 76, 5))
171+
>t13.foo : Symbol(C.foo, Decl(a.js, 64, 21))
172+
>t13 : Symbol(t13, Decl(a.js, 75, 5))
173+
>foo : Symbol(C.foo, Decl(a.js, 64, 21))
174+
175+
/**
176+
* @template {readonly unknown[]} const T
177+
* @param {T} args
178+
* @returns {T}
179+
*/
180+
function f6(...args) {
181+
>f6 : Symbol(f6, Decl(a.js, 76, 39))
182+
>args : Symbol(args, Decl(a.js, 83, 12))
183+
184+
return args;
185+
>args : Symbol(args, Decl(a.js, 83, 12))
186+
}
187+
const t15 = f6(1, 'b', { a: 1, b: 'x' });
188+
>t15 : Symbol(t15, Decl(a.js, 86, 5))
189+
>f6 : Symbol(f6, Decl(a.js, 76, 39))
190+
>a : Symbol(a, Decl(a.js, 86, 24))
191+
>b : Symbol(b, Decl(a.js, 86, 30))
192+

0 commit comments

Comments
 (0)