diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 96a35d5a4c77d..9087823617bca 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -3388,7 +3388,7 @@ namespace ts { function bindTypeParameter(node: TypeParameterDeclaration) { if (isJSDocTemplateTag(node.parent)) { - const container = find((node.parent.parent as JSDoc).tags!, isJSDocTypeAlias) || getHostSignatureFromJSDoc(node.parent); // TODO: GH#18217 + const container = getEffectiveContainerForJSDocTemplateTag(node.parent); if (container) { if (!container.locals) { container.locals = createSymbolTable(); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8ab4f678f4e87..3595696023b6f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2050,7 +2050,9 @@ namespace ts { lastSelfReferenceLocation = location; } lastLocation = location; - location = location.parent; + location = isJSDocTemplateTag(location) ? + getEffectiveContainerForJSDocTemplateTag(location) || location.parent : + location.parent; } // We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`. @@ -12855,7 +12857,7 @@ namespace ts { function getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol | undefined { const tp = getDeclarationOfKind(typeParameter.symbol, SyntaxKind.TypeParameter)!; - const host = isJSDocTemplateTag(tp.parent) ? getHostSignatureFromJSDoc(tp.parent) : tp.parent; + const host = isJSDocTemplateTag(tp.parent) ? getEffectiveContainerForJSDocTemplateTag(tp.parent) : tp.parent; return host && getSymbolOfNode(host); } @@ -33547,7 +33549,7 @@ namespace ts { } } - checkTypeParameters(node.typeParameters); + checkTypeParameters(getEffectiveTypeParameterDeclarations(node)); forEach(node.parameters, checkParameter); @@ -35180,6 +35182,7 @@ namespace ts { checkTypeNameIsReserved(node.name, Diagnostics.Type_alias_name_cannot_be_0); } checkSourceElement(node.typeExpression); + checkTypeParameters(getEffectiveTypeParameterDeclarations(node)); } function checkJSDocTemplateTag(node: JSDocTemplateTag): void { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 0aa0a8fb0957f..dbe5394908d48 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -8441,11 +8441,24 @@ namespace ts { function parseTemplateTagTypeParameter() { const typeParameterPos = getNodePos(); + const isBracketed = parseOptionalJsdoc(SyntaxKind.OpenBracketToken); + if (isBracketed) { + skipWhitespace(); + } const name = parseJSDocIdentifierName(Diagnostics.Unexpected_token_A_type_parameter_name_was_expected_without_curly_braces); + + let defaultType: TypeNode | undefined; + if (isBracketed) { + skipWhitespace(); + parseExpected(SyntaxKind.EqualsToken); + defaultType = doInsideOfContext(NodeFlags.JSDoc, parseJSDocType); + parseExpected(SyntaxKind.CloseBracketToken); + } + if (nodeIsMissing(name)) { return undefined; } - return finishNode(factory.createTypeParameterDeclaration(name, /*constraint*/ undefined, /*defaultType*/ undefined), typeParameterPos); + return finishNode(factory.createTypeParameterDeclaration(name, /*constraint*/ undefined, defaultType), typeParameterPos); } function parseTemplateTagTypeParameters() { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 58c43594069e9..cb0e26fbaec75 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2703,6 +2703,18 @@ namespace ts { return parameter && parameter.symbol; } + export function getEffectiveContainerForJSDocTemplateTag(node: JSDocTemplateTag) { + if (isJSDoc(node.parent) && node.parent.tags) { + // A @template tag belongs to any @typedef, @callback, or @enum tags in the same comment block, if they exist. + const typeAlias = find(node.parent.tags, isJSDocTypeAlias); + if (typeAlias) { + return typeAlias; + } + } + // otherwise it belongs to the host it annotates + return getHostSignatureFromJSDoc(node); + } + export function getHostSignatureFromJSDoc(node: Node): SignatureDeclaration | undefined { const host = getEffectiveJSDocHost(node); return host && isFunctionLike(host) ? host : undefined; diff --git a/tests/baselines/reference/completionEntryForUnionMethod.baseline b/tests/baselines/reference/completionEntryForUnionMethod.baseline index beda7838c3895..8bfd544e98a6d 100644 --- a/tests/baselines/reference/completionEntryForUnionMethod.baseline +++ b/tests/baselines/reference/completionEntryForUnionMethod.baseline @@ -1722,7 +1722,7 @@ "kind": "space" }, { - "text": "Function used to determine the order of the elements. It is expected to return\r\na negative value if first argument is less than second argument, zero if they're equal and a positive\r\nvalue otherwise. If omitted, the elements are sorted in ascending, ASCII character order.\r\n```ts\r\n[11,2,22,1].sort((a, b) => a - b)\r\n```", + "text": "Function used to determine the order of the elements. It is expected to return\r\na negative value if the first argument is less than the second argument, zero if they're equal, and a positive\r\nvalue otherwise. If omitted, the elements are sorted in ascending, ASCII character order.\r\n```ts\r\n[11,2,22,1].sort((a, b) => a - b)\r\n```", "kind": "text" } ] diff --git a/tests/baselines/reference/jsdocTemplateTagDefault.errors.txt b/tests/baselines/reference/jsdocTemplateTagDefault.errors.txt new file mode 100644 index 0000000000000..d21f546ed2dd4 --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateTagDefault.errors.txt @@ -0,0 +1,90 @@ +tests/cases/conformance/jsdoc/file.js(9,20): error TS2322: Type 'number' is not assignable to type 'string'. +tests/cases/conformance/jsdoc/file.js(22,34): error TS1005: '=' expected. +tests/cases/conformance/jsdoc/file.js(27,35): error TS1110: Type expected. +tests/cases/conformance/jsdoc/file.js(33,14): error TS2706: Required type parameters may not follow optional type parameters. +tests/cases/conformance/jsdoc/file.js(38,17): error TS2744: Type parameter defaults can only reference previously declared type parameters. +tests/cases/conformance/jsdoc/file.js(53,14): error TS2706: Required type parameters may not follow optional type parameters. +tests/cases/conformance/jsdoc/file.js(60,17): error TS2744: Type parameter defaults can only reference previously declared type parameters. + + +==== tests/cases/conformance/jsdoc/file.js (7 errors) ==== + /** + * @template {string | number} [T=string] - ok: defaults are permitted + * @typedef {[T]} A + */ + + /** @type {A} */ // ok, default for `T` in `A` is `string` + const aDefault1 = [""]; + /** @type {A} */ // error: `number` is not assignable to string` + const aDefault2 = [0]; + ~ +!!! error TS2322: Type 'number' is not assignable to type 'string'. + /** @type {A} */ // ok, `T` is provided for `A` + const aString = [""]; + /** @type {A} */ // ok, `T` is provided for `A` + const aNumber = [0]; + + /** + * @template T + * @template [U=T] - ok: default can reference earlier type parameter + * @typedef {[T, U]} B + */ + + /** + * @template {string | number} [T] - error: default requires an `=type` + ~ +!!! error TS1005: '=' expected. + * @typedef {[T]} C + */ + + /** + * @template {string | number} [T=] - error: default requires a `type` + ~ +!!! error TS1110: Type expected. + * @typedef {[T]} D + */ + + /** + * @template {string | number} [T=string] + * @template U - error: Required type parameters cannot follow optional type parameters + ~ +!!! error TS2706: Required type parameters may not follow optional type parameters. + * @typedef {[T, U]} E + */ + + /** + * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. + ~ +!!! error TS2744: Type parameter defaults can only reference previously declared type parameters. + * @template [U=T] + * @typedef {[T, U]} G + */ + + /** + * @template T + * @template [U=T] - ok: default can reference earlier type parameter + * @param {T} a + * @param {U} b + */ + function f1(a, b) {} + + /** + * @template {string | number} [T=string] + * @template U - error: Required type parameters cannot follow optional type parameters + ~ +!!! error TS2706: Required type parameters may not follow optional type parameters. + * @param {T} a + * @param {U} b + */ + function f2(a, b) {} + + /** + * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. + ~ +!!! error TS2744: Type parameter defaults can only reference previously declared type parameters. + * @template [U=T] + * @param {T} a + * @param {U} b + */ + function f3(a, b) {} + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocTemplateTagDefault.js b/tests/baselines/reference/jsdocTemplateTagDefault.js new file mode 100644 index 0000000000000..4c65f8f78d6ec --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateTagDefault.js @@ -0,0 +1,186 @@ +//// [file.js] +/** + * @template {string | number} [T=string] - ok: defaults are permitted + * @typedef {[T]} A + */ + +/** @type {A} */ // ok, default for `T` in `A` is `string` +const aDefault1 = [""]; +/** @type {A} */ // error: `number` is not assignable to string` +const aDefault2 = [0]; +/** @type {A} */ // ok, `T` is provided for `A` +const aString = [""]; +/** @type {A} */ // ok, `T` is provided for `A` +const aNumber = [0]; + +/** + * @template T + * @template [U=T] - ok: default can reference earlier type parameter + * @typedef {[T, U]} B + */ + +/** + * @template {string | number} [T] - error: default requires an `=type` + * @typedef {[T]} C + */ + +/** + * @template {string | number} [T=] - error: default requires a `type` + * @typedef {[T]} D + */ + +/** + * @template {string | number} [T=string] + * @template U - error: Required type parameters cannot follow optional type parameters + * @typedef {[T, U]} E + */ + +/** + * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. + * @template [U=T] + * @typedef {[T, U]} G + */ + +/** + * @template T + * @template [U=T] - ok: default can reference earlier type parameter + * @param {T} a + * @param {U} b + */ +function f1(a, b) {} + + /** + * @template {string | number} [T=string] + * @template U - error: Required type parameters cannot follow optional type parameters + * @param {T} a + * @param {U} b + */ +function f2(a, b) {} + +/** + * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. + * @template [U=T] + * @param {T} a + * @param {U} b + */ +function f3(a, b) {} + + +//// [file.js] +/** + * @template {string | number} [T=string] - ok: defaults are permitted + * @typedef {[T]} A + */ +/** @type {A} */ // ok, default for `T` in `A` is `string` +var aDefault1 = [""]; +/** @type {A} */ // error: `number` is not assignable to string` +var aDefault2 = [0]; +/** @type {A} */ // ok, `T` is provided for `A` +var aString = [""]; +/** @type {A} */ // ok, `T` is provided for `A` +var aNumber = [0]; +/** + * @template T + * @template [U=T] - ok: default can reference earlier type parameter + * @typedef {[T, U]} B + */ +/** + * @template {string | number} [T] - error: default requires an `=type` + * @typedef {[T]} C + */ +/** + * @template {string | number} [T=] - error: default requires a `type` + * @typedef {[T]} D + */ +/** + * @template {string | number} [T=string] + * @template U - error: Required type parameters cannot follow optional type parameters + * @typedef {[T, U]} E + */ +/** + * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. + * @template [U=T] + * @typedef {[T, U]} G + */ +/** + * @template T + * @template [U=T] - ok: default can reference earlier type parameter + * @param {T} a + * @param {U} b + */ +function f1(a, b) { } +/** +* @template {string | number} [T=string] +* @template U - error: Required type parameters cannot follow optional type parameters +* @param {T} a +* @param {U} b +*/ +function f2(a, b) { } +/** + * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. + * @template [U=T] + * @param {T} a + * @param {U} b + */ +function f3(a, b) { } + + +//// [file.d.ts] +/** + * @template T + * @template [U=T] - ok: default can reference earlier type parameter + * @typedef {[T, U]} B + */ +/** + * @template {string | number} [T] - error: default requires an `=type` + * @typedef {[T]} C + */ +/** + * @template {string | number} [T=] - error: default requires a `type` + * @typedef {[T]} D + */ +/** + * @template {string | number} [T=string] + * @template U - error: Required type parameters cannot follow optional type parameters + * @typedef {[T, U]} E + */ +/** + * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. + * @template [U=T] + * @typedef {[T, U]} G + */ +/** + * @template T + * @template [U=T] - ok: default can reference earlier type parameter + * @param {T} a + * @param {U} b + */ +declare function f1(a: T, b: U): void; +/** +* @template {string | number} [T=string] +* @template U - error: Required type parameters cannot follow optional type parameters +* @param {T} a +* @param {U} b +*/ +declare function f2(a: T, b: U): void; +/** + * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. + * @template [U=T] + * @param {T} a + * @param {U} b + */ +declare function f3(a: T, b: U): void; +/** + * @template {string | number} [T=string] - ok: defaults are permitted + * @typedef {[T]} A + */ +/** @type {A} */ declare const aDefault1: A; +/** @type {A} */ declare const aDefault2: A; +/** @type {A} */ declare const aString: A; +/** @type {A} */ declare const aNumber: A; +type B = [T, U]; +type C = [T]; +type D = [T]; +type E = [T, U]; +type G = [T, U]; +type A = [T]; diff --git a/tests/baselines/reference/jsdocTemplateTagDefault.symbols b/tests/baselines/reference/jsdocTemplateTagDefault.symbols new file mode 100644 index 0000000000000..19cba3a6a0474 --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateTagDefault.symbols @@ -0,0 +1,83 @@ +=== tests/cases/conformance/jsdoc/file.js === +/** + * @template {string | number} [T=string] - ok: defaults are permitted + * @typedef {[T]} A + */ + +/** @type {A} */ // ok, default for `T` in `A` is `string` +const aDefault1 = [""]; +>aDefault1 : Symbol(aDefault1, Decl(file.js, 6, 5)) + +/** @type {A} */ // error: `number` is not assignable to string` +const aDefault2 = [0]; +>aDefault2 : Symbol(aDefault2, Decl(file.js, 8, 5)) + +/** @type {A} */ // ok, `T` is provided for `A` +const aString = [""]; +>aString : Symbol(aString, Decl(file.js, 10, 5)) + +/** @type {A} */ // ok, `T` is provided for `A` +const aNumber = [0]; +>aNumber : Symbol(aNumber, Decl(file.js, 12, 5)) + +/** + * @template T + * @template [U=T] - ok: default can reference earlier type parameter + * @typedef {[T, U]} B + */ + +/** + * @template {string | number} [T] - error: default requires an `=type` + * @typedef {[T]} C + */ + +/** + * @template {string | number} [T=] - error: default requires a `type` + * @typedef {[T]} D + */ + +/** + * @template {string | number} [T=string] + * @template U - error: Required type parameters cannot follow optional type parameters + * @typedef {[T, U]} E + */ + +/** + * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. + * @template [U=T] + * @typedef {[T, U]} G + */ + +/** + * @template T + * @template [U=T] - ok: default can reference earlier type parameter + * @param {T} a + * @param {U} b + */ +function f1(a, b) {} +>f1 : Symbol(f1, Decl(file.js, 12, 20)) +>a : Symbol(a, Decl(file.js, 48, 12)) +>b : Symbol(b, Decl(file.js, 48, 14)) + + /** + * @template {string | number} [T=string] + * @template U - error: Required type parameters cannot follow optional type parameters + * @param {T} a + * @param {U} b + */ +function f2(a, b) {} +>f2 : Symbol(f2, Decl(file.js, 48, 20)) +>a : Symbol(a, Decl(file.js, 56, 12)) +>b : Symbol(b, Decl(file.js, 56, 14)) + +/** + * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. + * @template [U=T] + * @param {T} a + * @param {U} b + */ +function f3(a, b) {} +>f3 : Symbol(f3, Decl(file.js, 56, 20)) +>a : Symbol(a, Decl(file.js, 64, 12)) +>b : Symbol(b, Decl(file.js, 64, 14)) + diff --git a/tests/baselines/reference/jsdocTemplateTagDefault.types b/tests/baselines/reference/jsdocTemplateTagDefault.types new file mode 100644 index 0000000000000..7b00202e84268 --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateTagDefault.types @@ -0,0 +1,91 @@ +=== tests/cases/conformance/jsdoc/file.js === +/** + * @template {string | number} [T=string] - ok: defaults are permitted + * @typedef {[T]} A + */ + +/** @type {A} */ // ok, default for `T` in `A` is `string` +const aDefault1 = [""]; +>aDefault1 : A +>[""] : [string] +>"" : "" + +/** @type {A} */ // error: `number` is not assignable to string` +const aDefault2 = [0]; +>aDefault2 : A +>[0] : [number] +>0 : 0 + +/** @type {A} */ // ok, `T` is provided for `A` +const aString = [""]; +>aString : A +>[""] : [string] +>"" : "" + +/** @type {A} */ // ok, `T` is provided for `A` +const aNumber = [0]; +>aNumber : A +>[0] : [number] +>0 : 0 + +/** + * @template T + * @template [U=T] - ok: default can reference earlier type parameter + * @typedef {[T, U]} B + */ + +/** + * @template {string | number} [T] - error: default requires an `=type` + * @typedef {[T]} C + */ + +/** + * @template {string | number} [T=] - error: default requires a `type` + * @typedef {[T]} D + */ + +/** + * @template {string | number} [T=string] + * @template U - error: Required type parameters cannot follow optional type parameters + * @typedef {[T, U]} E + */ + +/** + * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. + * @template [U=T] + * @typedef {[T, U]} G + */ + +/** + * @template T + * @template [U=T] - ok: default can reference earlier type parameter + * @param {T} a + * @param {U} b + */ +function f1(a, b) {} +>f1 : (a: T, b: U) => void +>a : T +>b : U + + /** + * @template {string | number} [T=string] + * @template U - error: Required type parameters cannot follow optional type parameters + * @param {T} a + * @param {U} b + */ +function f2(a, b) {} +>f2 : (a: T, b: U) => void +>a : T +>b : U + +/** + * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. + * @template [U=T] + * @param {T} a + * @param {U} b + */ +function f3(a, b) {} +>f3 : (a: T, b: U) => void +>a : T +>b : U + diff --git a/tests/baselines/reference/jsdocTemplateTagNameResolution.errors.txt b/tests/baselines/reference/jsdocTemplateTagNameResolution.errors.txt new file mode 100644 index 0000000000000..7c23e7abacad7 --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateTagNameResolution.errors.txt @@ -0,0 +1,16 @@ +tests/cases/conformance/jsdoc/file.js(10,7): error TS2322: Type 'string' is not assignable to type 'number'. + + +==== tests/cases/conformance/jsdoc/file.js (1 errors) ==== + /** + * @template T + * @template {keyof T} K + * @typedef {T[K]} Foo + */ + + const x = { a: 1 }; + + /** @type {Foo} */ + const y = "a"; + ~ +!!! error TS2322: Type 'string' is not assignable to type 'number'. \ No newline at end of file diff --git a/tests/baselines/reference/jsdocTemplateTagNameResolution.js b/tests/baselines/reference/jsdocTemplateTagNameResolution.js new file mode 100644 index 0000000000000..0db2a96eb5505 --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateTagNameResolution.js @@ -0,0 +1,30 @@ +//// [file.js] +/** + * @template T + * @template {keyof T} K + * @typedef {T[K]} Foo + */ + +const x = { a: 1 }; + +/** @type {Foo} */ +const y = "a"; + +//// [file.js] +/** + * @template T + * @template {keyof T} K + * @typedef {T[K]} Foo + */ +var x = { a: 1 }; +/** @type {Foo} */ +var y = "a"; + + +//// [file.d.ts] +declare namespace x { + const a: number; +} +/** @type {Foo} */ +declare const y: Foo; +type Foo = T[K]; diff --git a/tests/baselines/reference/jsdocTemplateTagNameResolution.symbols b/tests/baselines/reference/jsdocTemplateTagNameResolution.symbols new file mode 100644 index 0000000000000..3252fee63ba24 --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateTagNameResolution.symbols @@ -0,0 +1,15 @@ +=== tests/cases/conformance/jsdoc/file.js === +/** + * @template T + * @template {keyof T} K + * @typedef {T[K]} Foo + */ + +const x = { a: 1 }; +>x : Symbol(x, Decl(file.js, 6, 5)) +>a : Symbol(a, Decl(file.js, 6, 11)) + +/** @type {Foo} */ +const y = "a"; +>y : Symbol(y, Decl(file.js, 9, 5)) + diff --git a/tests/baselines/reference/jsdocTemplateTagNameResolution.types b/tests/baselines/reference/jsdocTemplateTagNameResolution.types new file mode 100644 index 0000000000000..ea9ec471e13ad --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateTagNameResolution.types @@ -0,0 +1,18 @@ +=== tests/cases/conformance/jsdoc/file.js === +/** + * @template T + * @template {keyof T} K + * @typedef {T[K]} Foo + */ + +const x = { a: 1 }; +>x : { a: number; } +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 + +/** @type {Foo} */ +const y = "a"; +>y : number +>"a" : "a" + diff --git a/tests/cases/conformance/jsdoc/jsdocTemplateTagDefault.ts b/tests/cases/conformance/jsdoc/jsdocTemplateTagDefault.ts new file mode 100644 index 0000000000000..c93359a7a6eb3 --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocTemplateTagDefault.ts @@ -0,0 +1,71 @@ +// @allowJs: true +// @checkJs: true +// @declaration: true +// @outDir: out +// @Filename: file.js + +/** + * @template {string | number} [T=string] - ok: defaults are permitted + * @typedef {[T]} A + */ + +/** @type {A} */ // ok, default for `T` in `A` is `string` +const aDefault1 = [""]; +/** @type {A} */ // error: `number` is not assignable to string` +const aDefault2 = [0]; +/** @type {A} */ // ok, `T` is provided for `A` +const aString = [""]; +/** @type {A} */ // ok, `T` is provided for `A` +const aNumber = [0]; + +/** + * @template T + * @template [U=T] - ok: default can reference earlier type parameter + * @typedef {[T, U]} B + */ + +/** + * @template {string | number} [T] - error: default requires an `=type` + * @typedef {[T]} C + */ + +/** + * @template {string | number} [T=] - error: default requires a `type` + * @typedef {[T]} D + */ + +/** + * @template {string | number} [T=string] + * @template U - error: Required type parameters cannot follow optional type parameters + * @typedef {[T, U]} E + */ + +/** + * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. + * @template [U=T] + * @typedef {[T, U]} G + */ + +/** + * @template T + * @template [U=T] - ok: default can reference earlier type parameter + * @param {T} a + * @param {U} b + */ +function f1(a, b) {} + + /** + * @template {string | number} [T=string] + * @template U - error: Required type parameters cannot follow optional type parameters + * @param {T} a + * @param {U} b + */ +function f2(a, b) {} + +/** + * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. + * @template [U=T] + * @param {T} a + * @param {U} b + */ +function f3(a, b) {} diff --git a/tests/cases/conformance/jsdoc/jsdocTemplateTagNameResolution.ts b/tests/cases/conformance/jsdoc/jsdocTemplateTagNameResolution.ts new file mode 100644 index 0000000000000..a5b4d052ad2de --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocTemplateTagNameResolution.ts @@ -0,0 +1,16 @@ +// @allowJs: true +// @checkJs: true +// @outDir: out +// @declaration: true +// @Filename: file.js + +/** + * @template T + * @template {keyof T} K + * @typedef {T[K]} Foo + */ + +const x = { a: 1 }; + +/** @type {Foo} */ +const y = "a"; \ No newline at end of file