diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0f4db171ab0fb..0c7d4044a4177 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28416,9 +28416,9 @@ namespace ts { } function checkGrammarConstructorTypeParameters(node: ConstructorDeclaration) { - const typeParameters = getEffectiveTypeParameterDeclarations(node); - if (isNodeArray(typeParameters)) { - const { pos, end } = typeParameters; + const jsdocTypeParameters = isInJavaScriptFile(node) && getJSDocTypeParameterDeclarations(node); + if (node.typeParameters || jsdocTypeParameters && jsdocTypeParameters.length) { + const { pos, end } = node.typeParameters || jsdocTypeParameters && jsdocTypeParameters[0] || node; return grammarErrorAtPos(node, pos, end - pos, Diagnostics.Type_parameters_cannot_appear_on_a_constructor_declaration); } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 259c1ee4ea5f4..79d91f7637941 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -219,6 +219,10 @@ "category": "Error", "code": 1068 }, + "Unexpected token. A type parameter name was expected without curly braces.": { + "category": "Error", + "code": 1069 + }, "'{0}' modifier cannot appear on a type member.": { "category": "Error", "code": 1070 diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index ea5a2a339372a..40ec484afb862 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -7013,29 +7013,27 @@ namespace ts { } function parseTemplateTag(atToken: AtToken, tagName: Identifier): JSDocTemplateTag | undefined { - if (some(tags, isJSDocTemplateTag)) { - parseErrorAt(tagName.pos, scanner.getTokenPos(), Diagnostics._0_tag_already_specified, tagName.escapedText); + // the template tag looks like '@template {Constraint} T,U,V' + let constraint: JSDocTypeExpression | undefined; + if (token() === SyntaxKind.OpenBraceToken) { + constraint = parseJSDocTypeExpression(); + skipWhitespace(); } - // Type parameter list looks like '@template T,U,V' const typeParameters = []; const typeParametersPos = getNodePos(); - while (true) { const typeParameter = createNode(SyntaxKind.TypeParameter); - const name = parseJSDocIdentifierNameWithOptionalBraces(); - skipWhitespace(); - if (!name) { - parseErrorAtPosition(scanner.getStartPos(), 0, Diagnostics.Identifier_expected); + if (!tokenIsIdentifierOrKeyword(token())) { + parseErrorAtCurrentToken(Diagnostics.Unexpected_token_A_type_parameter_name_was_expected_without_curly_braces); return undefined; } - - typeParameter.name = name; + typeParameter.name = parseJSDocIdentifierName()!; + skipWhitespace(); finishNode(typeParameter); - typeParameters.push(typeParameter); - if (token() === SyntaxKind.CommaToken) { + // need to look for more type parameters nextJSDocToken(); skipWhitespace(); } @@ -7044,6 +7042,10 @@ namespace ts { } } + if (constraint) { + first(typeParameters).constraint = constraint.type; + } + const result = createNode(SyntaxKind.JSDocTemplateTag, atToken.pos); result.atToken = atToken; result.tagName = tagName; @@ -7052,15 +7054,6 @@ namespace ts { return result; } - function parseJSDocIdentifierNameWithOptionalBraces(): Identifier | undefined { - const parsedBrace = parseOptional(SyntaxKind.OpenBraceToken); - const res = parseJSDocIdentifierName(); - if (parsedBrace) { - parseExpected(SyntaxKind.CloseBraceToken); - } - return res; - } - function nextJSDocToken(): JsDocSyntaxKind { return currentToken = scanner.scanJSDocToken(); } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 8b4e12dc2220b..65984a5cedb4b 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3166,21 +3166,18 @@ namespace ts { } if (isJSDocTypeAlias(node)) { Debug.assert(node.parent.kind === SyntaxKind.JSDocComment); - const templateTags = flatMap(filter(node.parent.tags, isJSDocTemplateTag), tag => tag.typeParameters) as ReadonlyArray; - const templateTagNodes = templateTags as NodeArray; - templateTagNodes.pos = templateTagNodes.length > 0 ? first(templateTagNodes).pos : node.pos; - templateTagNodes.end = templateTagNodes.length > 0 ? last(templateTagNodes).end : node.end; - templateTagNodes.hasTrailingComma = false; - return templateTagNodes; + return flatMap(node.parent.tags, tag => isJSDocTemplateTag(tag) ? tag.typeParameters : undefined) as ReadonlyArray; } return node.typeParameters || (isInJavaScriptFile(node) ? getJSDocTypeParameterDeclarations(node) : emptyArray); } export function getJSDocTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray { - // template tags are only available when a typedef isn't already using them - const tag = find(getJSDocTags(node), (tag): tag is JSDocTemplateTag => - isJSDocTemplateTag(tag) && !(tag.parent.kind === SyntaxKind.JSDocComment && tag.parent.tags!.some(isJSDocTypeAlias))); - return (tag && tag.typeParameters) || emptyArray; + return flatMap(getJSDocTags(node), tag => isNonTypeAliasTemplate(tag) ? tag.typeParameters : undefined); + } + + /** template tags are only available when a typedef isn't already using them */ + function isNonTypeAliasTemplate(tag: JSDocTag): tag is JSDocTemplateTag { + return isJSDocTemplateTag(tag) && !(tag.parent.kind === SyntaxKind.JSDocComment && tag.parent.tags!.some(isJSDocTypeAlias)); } /** diff --git a/tests/baselines/reference/jsdocTemplateClass.errors.txt b/tests/baselines/reference/jsdocTemplateClass.errors.txt index 402992c34bdc9..372fee6fd5a81 100644 --- a/tests/baselines/reference/jsdocTemplateClass.errors.txt +++ b/tests/baselines/reference/jsdocTemplateClass.errors.txt @@ -3,7 +3,7 @@ tests/cases/conformance/jsdoc/templateTagOnClasses.js(25,1): error TS2322: Type ==== tests/cases/conformance/jsdoc/templateTagOnClasses.js (1 errors) ==== /** - * @template {T} + * @template T * @typedef {(t: T) => T} Id */ /** @template T */ diff --git a/tests/baselines/reference/jsdocTemplateClass.symbols b/tests/baselines/reference/jsdocTemplateClass.symbols index b82cd8038e50b..a2da2f051cd3e 100644 --- a/tests/baselines/reference/jsdocTemplateClass.symbols +++ b/tests/baselines/reference/jsdocTemplateClass.symbols @@ -1,6 +1,6 @@ === tests/cases/conformance/jsdoc/templateTagOnClasses.js === /** - * @template {T} + * @template T * @typedef {(t: T) => T} Id */ /** @template T */ diff --git a/tests/baselines/reference/jsdocTemplateClass.types b/tests/baselines/reference/jsdocTemplateClass.types index 9c028a96ff170..40b73bc700266 100644 --- a/tests/baselines/reference/jsdocTemplateClass.types +++ b/tests/baselines/reference/jsdocTemplateClass.types @@ -1,6 +1,6 @@ === tests/cases/conformance/jsdoc/templateTagOnClasses.js === /** - * @template {T} + * @template T * @typedef {(t: T) => T} Id */ /** @template T */ diff --git a/tests/baselines/reference/jsdocTemplateConstructorFunction.errors.txt b/tests/baselines/reference/jsdocTemplateConstructorFunction.errors.txt index 9e735b780a0fc..1153424c8db61 100644 --- a/tests/baselines/reference/jsdocTemplateConstructorFunction.errors.txt +++ b/tests/baselines/reference/jsdocTemplateConstructorFunction.errors.txt @@ -3,12 +3,12 @@ tests/cases/conformance/jsdoc/templateTagOnConstructorFunctions.js(24,1): error ==== tests/cases/conformance/jsdoc/templateTagOnConstructorFunctions.js (1 errors) ==== /** - * @template {U} + * @template U * @typedef {(u: U) => U} Id */ /** * @param {T} t - * @template {T} + * @template T */ function Zet(t) { /** @type {T} */ diff --git a/tests/baselines/reference/jsdocTemplateConstructorFunction.symbols b/tests/baselines/reference/jsdocTemplateConstructorFunction.symbols index b640d3c1e21e4..86b80ca08b620 100644 --- a/tests/baselines/reference/jsdocTemplateConstructorFunction.symbols +++ b/tests/baselines/reference/jsdocTemplateConstructorFunction.symbols @@ -1,11 +1,11 @@ === tests/cases/conformance/jsdoc/templateTagOnConstructorFunctions.js === /** - * @template {U} + * @template U * @typedef {(u: U) => U} Id */ /** * @param {T} t - * @template {T} + * @template T */ function Zet(t) { >Zet : Symbol(Zet, Decl(templateTagOnConstructorFunctions.js, 0, 0)) diff --git a/tests/baselines/reference/jsdocTemplateConstructorFunction.types b/tests/baselines/reference/jsdocTemplateConstructorFunction.types index 121cf846a0e39..4c0237aedebe6 100644 --- a/tests/baselines/reference/jsdocTemplateConstructorFunction.types +++ b/tests/baselines/reference/jsdocTemplateConstructorFunction.types @@ -1,11 +1,11 @@ === tests/cases/conformance/jsdoc/templateTagOnConstructorFunctions.js === /** - * @template {U} + * @template U * @typedef {(u: U) => U} Id */ /** * @param {T} t - * @template {T} + * @template T */ function Zet(t) { >Zet : typeof Zet diff --git a/tests/baselines/reference/jsdocTemplateConstructorFunction2.errors.txt b/tests/baselines/reference/jsdocTemplateConstructorFunction2.errors.txt index cdcc9a056ebab..d7222f11ee28a 100644 --- a/tests/baselines/reference/jsdocTemplateConstructorFunction2.errors.txt +++ b/tests/baselines/reference/jsdocTemplateConstructorFunction2.errors.txt @@ -5,7 +5,7 @@ tests/cases/conformance/jsdoc/templateTagWithNestedTypeLiteral.js(26,15): error ==== tests/cases/conformance/jsdoc/templateTagWithNestedTypeLiteral.js (2 errors) ==== /** * @param {T} t - * @template {T} + * @template T */ function Zet(t) { /** @type {T} */ diff --git a/tests/baselines/reference/jsdocTemplateConstructorFunction2.symbols b/tests/baselines/reference/jsdocTemplateConstructorFunction2.symbols index 32573a373985b..ebc2e5eba58a5 100644 --- a/tests/baselines/reference/jsdocTemplateConstructorFunction2.symbols +++ b/tests/baselines/reference/jsdocTemplateConstructorFunction2.symbols @@ -1,7 +1,7 @@ === tests/cases/conformance/jsdoc/templateTagWithNestedTypeLiteral.js === /** * @param {T} t - * @template {T} + * @template T */ function Zet(t) { >Zet : Symbol(Zet, Decl(templateTagWithNestedTypeLiteral.js, 0, 0)) diff --git a/tests/baselines/reference/jsdocTemplateConstructorFunction2.types b/tests/baselines/reference/jsdocTemplateConstructorFunction2.types index 14407a5a0c9d4..91c820ce0947e 100644 --- a/tests/baselines/reference/jsdocTemplateConstructorFunction2.types +++ b/tests/baselines/reference/jsdocTemplateConstructorFunction2.types @@ -1,7 +1,7 @@ === tests/cases/conformance/jsdoc/templateTagWithNestedTypeLiteral.js === /** * @param {T} t - * @template {T} + * @template T */ function Zet(t) { >Zet : typeof Zet diff --git a/tests/baselines/reference/jsdocTemplateTag3.errors.txt b/tests/baselines/reference/jsdocTemplateTag3.errors.txt new file mode 100644 index 0000000000000..a85024e1499d1 --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateTag3.errors.txt @@ -0,0 +1,47 @@ +tests/cases/conformance/jsdoc/a.js(14,29): error TS2339: Property 'a' does not exist on type 'U'. +tests/cases/conformance/jsdoc/a.js(14,35): error TS2339: Property 'b' does not exist on type 'U'. +tests/cases/conformance/jsdoc/a.js(21,3): error TS2345: Argument of type '{ a: number; }' is not assignable to parameter of type '{ a: number; b: string; }'. + Property 'b' is missing in type '{ a: number; }'. +tests/cases/conformance/jsdoc/a.js(25,2): error TS1069: Unexpected token. A type parameter name was expected without curly braces. + + +==== tests/cases/conformance/jsdoc/a.js (4 errors) ==== + /** + * @template {{ a: number, b: string }} T,U A Comment + * @template {{ c: boolean }} V uh ... are comments even supported?? + * @template W + * @template X That last one had no comment + * @param {T} t + * @param {U} u + * @param {V} v + * @param {W} w + * @param {X} x + * @return {W | X} + */ + function f(t, u, v, w, x) { + if(t.a + t.b.length > u.a - u.b.length && v.c) { + ~ +!!! error TS2339: Property 'a' does not exist on type 'U'. + ~ +!!! error TS2339: Property 'b' does not exist on type 'U'. + return w; + } + return x; + } + + f({ a: 12, b: 'hi', c: null }, undefined, { c: false, d: 12, b: undefined }, 101, 'nope'); + f({ a: 12 }, undefined, undefined, 101, 'nope'); + ~~~~~~~~~~ +!!! error TS2345: Argument of type '{ a: number; }' is not assignable to parameter of type '{ a: number; b: string; }'. +!!! error TS2345: Property 'b' is missing in type '{ a: number; }'. + + /** + * @template {NoLongerAllowed} + * @template T preceding line's syntax is no longer allowed + ~ +!!! error TS1069: Unexpected token. A type parameter name was expected without curly braces. + * @param {T} x + */ + function g(x) { } + + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocTemplateTag3.symbols b/tests/baselines/reference/jsdocTemplateTag3.symbols new file mode 100644 index 0000000000000..f9257d40e6fc8 --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateTag3.symbols @@ -0,0 +1,70 @@ +=== tests/cases/conformance/jsdoc/a.js === +/** + * @template {{ a: number, b: string }} T,U A Comment + * @template {{ c: boolean }} V uh ... are comments even supported?? + * @template W + * @template X That last one had no comment + * @param {T} t + * @param {U} u + * @param {V} v + * @param {W} w + * @param {X} x + * @return {W | X} + */ +function f(t, u, v, w, x) { +>f : Symbol(f, Decl(a.js, 0, 0)) +>t : Symbol(t, Decl(a.js, 12, 11)) +>u : Symbol(u, Decl(a.js, 12, 13)) +>v : Symbol(v, Decl(a.js, 12, 16)) +>w : Symbol(w, Decl(a.js, 12, 19)) +>x : Symbol(x, Decl(a.js, 12, 22)) + + if(t.a + t.b.length > u.a - u.b.length && v.c) { +>t.a : Symbol(a, Decl(a.js, 1, 15)) +>t : Symbol(t, Decl(a.js, 12, 11)) +>a : Symbol(a, Decl(a.js, 1, 15)) +>t.b.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>t.b : Symbol(b, Decl(a.js, 1, 26)) +>t : Symbol(t, Decl(a.js, 12, 11)) +>b : Symbol(b, Decl(a.js, 1, 26)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>u : Symbol(u, Decl(a.js, 12, 13)) +>u : Symbol(u, Decl(a.js, 12, 13)) +>v.c : Symbol(c, Decl(a.js, 2, 15)) +>v : Symbol(v, Decl(a.js, 12, 16)) +>c : Symbol(c, Decl(a.js, 2, 15)) + + return w; +>w : Symbol(w, Decl(a.js, 12, 19)) + } + return x; +>x : Symbol(x, Decl(a.js, 12, 22)) +} + +f({ a: 12, b: 'hi', c: null }, undefined, { c: false, d: 12, b: undefined }, 101, 'nope'); +>f : Symbol(f, Decl(a.js, 0, 0)) +>a : Symbol(a, Decl(a.js, 19, 3)) +>b : Symbol(b, Decl(a.js, 19, 10)) +>c : Symbol(c, Decl(a.js, 19, 19)) +>undefined : Symbol(undefined) +>c : Symbol(c, Decl(a.js, 19, 43)) +>d : Symbol(d, Decl(a.js, 19, 53)) +>b : Symbol(b, Decl(a.js, 19, 60)) +>undefined : Symbol(undefined) + +f({ a: 12 }, undefined, undefined, 101, 'nope'); +>f : Symbol(f, Decl(a.js, 0, 0)) +>a : Symbol(a, Decl(a.js, 20, 3)) +>undefined : Symbol(undefined) +>undefined : Symbol(undefined) + +/** + * @template {NoLongerAllowed} + * @template T preceding line's syntax is no longer allowed + * @param {T} x + */ +function g(x) { } +>g : Symbol(g, Decl(a.js, 20, 49)) +>x : Symbol(x, Decl(a.js, 27, 11)) + + diff --git a/tests/baselines/reference/jsdocTemplateTag3.types b/tests/baselines/reference/jsdocTemplateTag3.types new file mode 100644 index 0000000000000..63e8498d84aac --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateTag3.types @@ -0,0 +1,95 @@ +=== tests/cases/conformance/jsdoc/a.js === +/** + * @template {{ a: number, b: string }} T,U A Comment + * @template {{ c: boolean }} V uh ... are comments even supported?? + * @template W + * @template X That last one had no comment + * @param {T} t + * @param {U} u + * @param {V} v + * @param {W} w + * @param {X} x + * @return {W | X} + */ +function f(t, u, v, w, x) { +>f : (t: T, u: U, v: V, w: W, x: X) => W | X +>t : T +>u : U +>v : V +>w : W +>x : X + + if(t.a + t.b.length > u.a - u.b.length && v.c) { +>t.a + t.b.length > u.a - u.b.length && v.c : boolean +>t.a + t.b.length > u.a - u.b.length : boolean +>t.a + t.b.length : number +>t.a : number +>t : T +>a : number +>t.b.length : number +>t.b : string +>t : T +>b : string +>length : number +>u.a - u.b.length : number +>u.a : any +>u : U +>a : any +>u.b.length : any +>u.b : any +>u : U +>b : any +>length : any +>v.c : boolean +>v : V +>c : boolean + + return w; +>w : W + } + return x; +>x : X +} + +f({ a: 12, b: 'hi', c: null }, undefined, { c: false, d: 12, b: undefined }, 101, 'nope'); +>f({ a: 12, b: 'hi', c: null }, undefined, { c: false, d: 12, b: undefined }, 101, 'nope') : string | number +>f : (t: T, u: U, v: V, w: W, x: X) => W | X +>{ a: 12, b: 'hi', c: null } : { a: number; b: string; c: null; } +>a : number +>12 : 12 +>b : string +>'hi' : "hi" +>c : null +>null : null +>undefined : undefined +>{ c: false, d: 12, b: undefined } : { c: false; d: number; b: undefined; } +>c : false +>false : false +>d : number +>12 : 12 +>b : undefined +>undefined : undefined +>101 : 101 +>'nope' : "nope" + +f({ a: 12 }, undefined, undefined, 101, 'nope'); +>f({ a: 12 }, undefined, undefined, 101, 'nope') : any +>f : (t: T, u: U, v: V, w: W, x: X) => W | X +>{ a: 12 } : { a: number; } +>a : number +>12 : 12 +>undefined : undefined +>undefined : undefined +>101 : 101 +>'nope' : "nope" + +/** + * @template {NoLongerAllowed} + * @template T preceding line's syntax is no longer allowed + * @param {T} x + */ +function g(x) { } +>g : (x: T) => void +>x : T + + diff --git a/tests/baselines/reference/quickInfoJsDocTags.baseline b/tests/baselines/reference/quickInfoJsDocTags.baseline index 6019419e48945..b022204dd69a2 100644 --- a/tests/baselines/reference/quickInfoJsDocTags.baseline +++ b/tests/baselines/reference/quickInfoJsDocTags.baseline @@ -2,13 +2,13 @@ { "marker": { "fileName": "/tests/cases/fourslash/quickInfoJsDocTags.ts", - "position": 258 + "position": 256 }, "quickInfo": { "kind": "function", "kindModifiers": "", "textSpan": { - "start": 258, + "start": 256, "length": 3 }, "displayParts": [ @@ -78,7 +78,7 @@ }, { "name": "template", - "text": "{T} A template" + "text": "T A template" }, { "name": "type", diff --git a/tests/baselines/reference/typedefMultipleTypeParameters.errors.txt b/tests/baselines/reference/typedefMultipleTypeParameters.errors.txt new file mode 100644 index 0000000000000..e8d0e52ba0f7f --- /dev/null +++ b/tests/baselines/reference/typedefMultipleTypeParameters.errors.txt @@ -0,0 +1,32 @@ +tests/cases/conformance/jsdoc/a.js(16,12): error TS2314: Generic type 'Everything' requires 5 type argument(s). +tests/cases/conformance/jsdoc/test.ts(1,34): error TS2344: Type '{ a: number; }' does not satisfy the constraint '{ a: number; b: string; }'. + Property 'b' is missing in type '{ a: number; }'. + + +==== tests/cases/conformance/jsdoc/a.js (1 errors) ==== + /** + * @template {{ a: number, b: string }} T,U A Comment + * @template {{ c: boolean }} V uh ... are comments even supported?? + * @template W + * @template X That last one had no comment + * @typedef {{ t: T, u: U, v: V, w: W, x: X }} Everything + */ + + /** @type {Everything<{ a: number, b: 'hi', c: never }, undefined, { c: true, d: 1 }, number, string>} */ + var tuvwx; + + // TODO: will error when #24592 is fixed + /** @type {Everything<{ a: number }, undefined, { c: 1, d: 1 }, number, string>} */ + var wrong; + + /** @type {Everything<{ a: number }>} */ + ~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2314: Generic type 'Everything' requires 5 type argument(s). + var insufficient; + +==== tests/cases/conformance/jsdoc/test.ts (1 errors) ==== + declare var actually: Everything<{ a: number }, undefined, { c: 1, d: 1 }, number, string>; + ~~~~~~~~~~~~~~ +!!! error TS2344: Type '{ a: number; }' does not satisfy the constraint '{ a: number; b: string; }'. +!!! error TS2344: Property 'b' is missing in type '{ a: number; }'. + \ No newline at end of file diff --git a/tests/baselines/reference/typedefMultipleTypeParameters.symbols b/tests/baselines/reference/typedefMultipleTypeParameters.symbols new file mode 100644 index 0000000000000..9acd30fea1be4 --- /dev/null +++ b/tests/baselines/reference/typedefMultipleTypeParameters.symbols @@ -0,0 +1,30 @@ +=== tests/cases/conformance/jsdoc/a.js === +/** + * @template {{ a: number, b: string }} T,U A Comment + * @template {{ c: boolean }} V uh ... are comments even supported?? + * @template W + * @template X That last one had no comment + * @typedef {{ t: T, u: U, v: V, w: W, x: X }} Everything + */ + +/** @type {Everything<{ a: number, b: 'hi', c: never }, undefined, { c: true, d: 1 }, number, string>} */ +var tuvwx; +>tuvwx : Symbol(tuvwx, Decl(a.js, 9, 3)) + +// TODO: will error when #24592 is fixed +/** @type {Everything<{ a: number }, undefined, { c: 1, d: 1 }, number, string>} */ +var wrong; +>wrong : Symbol(wrong, Decl(a.js, 13, 3)) + +/** @type {Everything<{ a: number }>} */ +var insufficient; +>insufficient : Symbol(insufficient, Decl(a.js, 16, 3)) + +=== tests/cases/conformance/jsdoc/test.ts === +declare var actually: Everything<{ a: number }, undefined, { c: 1, d: 1 }, number, string>; +>actually : Symbol(actually, Decl(test.ts, 0, 11)) +>Everything : Symbol(Everything, Decl(a.js, 5, 3)) +>a : Symbol(a, Decl(test.ts, 0, 34)) +>c : Symbol(c, Decl(test.ts, 0, 61)) +>d : Symbol(d, Decl(test.ts, 0, 67)) + diff --git a/tests/baselines/reference/typedefMultipleTypeParameters.types b/tests/baselines/reference/typedefMultipleTypeParameters.types new file mode 100644 index 0000000000000..5bc38bb835bf8 --- /dev/null +++ b/tests/baselines/reference/typedefMultipleTypeParameters.types @@ -0,0 +1,30 @@ +=== tests/cases/conformance/jsdoc/a.js === +/** + * @template {{ a: number, b: string }} T,U A Comment + * @template {{ c: boolean }} V uh ... are comments even supported?? + * @template W + * @template X That last one had no comment + * @typedef {{ t: T, u: U, v: V, w: W, x: X }} Everything + */ + +/** @type {Everything<{ a: number, b: 'hi', c: never }, undefined, { c: true, d: 1 }, number, string>} */ +var tuvwx; +>tuvwx : { t: { a: number; b: "hi"; c: never; }; u: undefined; v: { c: true; d: 1; }; w: number; x: string; } + +// TODO: will error when #24592 is fixed +/** @type {Everything<{ a: number }, undefined, { c: 1, d: 1 }, number, string>} */ +var wrong; +>wrong : { t: { a: number; }; u: undefined; v: { c: 1; d: 1; }; w: number; x: string; } + +/** @type {Everything<{ a: number }>} */ +var insufficient; +>insufficient : any + +=== tests/cases/conformance/jsdoc/test.ts === +declare var actually: Everything<{ a: number }, undefined, { c: 1, d: 1 }, number, string>; +>actually : { t: { a: number; }; u: undefined; v: { c: 1; d: 1; }; w: number; x: string; } +>Everything : { t: T; u: U; v: V; w: W; x: X; } +>a : number +>c : 1 +>d : 1 + diff --git a/tests/cases/conformance/jsdoc/jsdocTemplateClass.ts b/tests/cases/conformance/jsdoc/jsdocTemplateClass.ts index b13303d6def4f..08297262ccade 100644 --- a/tests/cases/conformance/jsdoc/jsdocTemplateClass.ts +++ b/tests/cases/conformance/jsdoc/jsdocTemplateClass.ts @@ -4,7 +4,7 @@ // @Filename: templateTagOnClasses.js /** - * @template {T} + * @template T * @typedef {(t: T) => T} Id */ /** @template T */ diff --git a/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction.ts b/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction.ts index a2a7a48c1cb7c..016607490a8f9 100644 --- a/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction.ts +++ b/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction.ts @@ -4,12 +4,12 @@ // @Filename: templateTagOnConstructorFunctions.js /** - * @template {U} + * @template U * @typedef {(u: U) => U} Id */ /** * @param {T} t - * @template {T} + * @template T */ function Zet(t) { /** @type {T} */ diff --git a/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction2.ts b/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction2.ts index c736962de3974..d0418661bb712 100644 --- a/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction2.ts +++ b/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction2.ts @@ -5,7 +5,7 @@ /** * @param {T} t - * @template {T} + * @template T */ function Zet(t) { /** @type {T} */ diff --git a/tests/cases/conformance/jsdoc/jsdocTemplateTag3.ts b/tests/cases/conformance/jsdoc/jsdocTemplateTag3.ts new file mode 100644 index 0000000000000..e05438d304de7 --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocTemplateTag3.ts @@ -0,0 +1,33 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true +// @Filename: a.js +/** + * @template {{ a: number, b: string }} T,U A Comment + * @template {{ c: boolean }} V uh ... are comments even supported?? + * @template W + * @template X That last one had no comment + * @param {T} t + * @param {U} u + * @param {V} v + * @param {W} w + * @param {X} x + * @return {W | X} + */ +function f(t, u, v, w, x) { + if(t.a + t.b.length > u.a - u.b.length && v.c) { + return w; + } + return x; +} + +f({ a: 12, b: 'hi', c: null }, undefined, { c: false, d: 12, b: undefined }, 101, 'nope'); +f({ a: 12 }, undefined, undefined, 101, 'nope'); + +/** + * @template {NoLongerAllowed} + * @template T preceding line's syntax is no longer allowed + * @param {T} x + */ +function g(x) { } + diff --git a/tests/cases/conformance/jsdoc/typedefMultipleTypeParameters.ts b/tests/cases/conformance/jsdoc/typedefMultipleTypeParameters.ts new file mode 100644 index 0000000000000..9fc7c0d0d4175 --- /dev/null +++ b/tests/cases/conformance/jsdoc/typedefMultipleTypeParameters.ts @@ -0,0 +1,24 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true +// @Filename: a.js +/** + * @template {{ a: number, b: string }} T,U A Comment + * @template {{ c: boolean }} V uh ... are comments even supported?? + * @template W + * @template X That last one had no comment + * @typedef {{ t: T, u: U, v: V, w: W, x: X }} Everything + */ + +/** @type {Everything<{ a: number, b: 'hi', c: never }, undefined, { c: true, d: 1 }, number, string>} */ +var tuvwx; + +// TODO: will error when #24592 is fixed +/** @type {Everything<{ a: number }, undefined, { c: 1, d: 1 }, number, string>} */ +var wrong; + +/** @type {Everything<{ a: number }>} */ +var insufficient; + +// @Filename: test.ts +declare var actually: Everything<{ a: number }, undefined, { c: 1, d: 1 }, number, string>; diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc9.5.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc9.5.ts index 33750e4f3e04b..d0319bee614c1 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc9.5.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc9.5.ts @@ -1,7 +1,7 @@ /// /////** -//// * @template {T} +//// * @template T //// * @param {T} x //// * @returns {T} //// */ @@ -11,7 +11,7 @@ verify.codeFix({ description: "Annotate with type from JSDoc", newFileContent: `/** - * @template {T} + * @template T * @param {T} x * @returns {T} */ diff --git a/tests/cases/fourslash/jsdocReturnsTag.ts b/tests/cases/fourslash/jsdocReturnsTag.ts index e91d350922278..3f78e275e93fe 100644 --- a/tests/cases/fourslash/jsdocReturnsTag.ts +++ b/tests/cases/fourslash/jsdocReturnsTag.ts @@ -17,7 +17,7 @@ verify.signatureHelp({ text: "find(l: T[], x: T): T", docComment: "Find an item", tags: [ - // TODO: GH#24130 + // TODO: GH#24130 (see PR #24600's commits for potential fix) { name: "template", text: "T\n " }, { name: "param", text: "l" }, { name: "param", text: "x" }, diff --git a/tests/cases/fourslash/quickInfoJsDocTags.ts b/tests/cases/fourslash/quickInfoJsDocTags.ts index ce045ab29400f..798a850a310fe 100644 --- a/tests/cases/fourslash/quickInfoJsDocTags.ts +++ b/tests/cases/fourslash/quickInfoJsDocTags.ts @@ -5,7 +5,7 @@ //// * Doc //// * @author Me //// * @augments {C} Augments it -//// * @template {T} A template +//// * @template T A template //// * @type {number | string} A type //// * @typedef {number | string} NumOrStr //// * @property {number} x The prop