From 3d9187f0ea2044186e42621e3838bfb8b7e989be Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 16 Aug 2021 19:19:38 -0700 Subject: [PATCH 1/4] Fix name resolution in typedef and allow defaults for template tags --- src/compiler/binder.ts | 2 +- src/compiler/checker.ts | 9 +- src/compiler/parser.ts | 20 +++- src/compiler/utilities.ts | 12 +++ .../jsdocTemplateTagDefault.errors.txt | 99 +++++++++++++++++++ .../reference/jsdocTemplateTagDefault.symbols | 89 +++++++++++++++++ .../reference/jsdocTemplateTagDefault.types | 97 ++++++++++++++++++ .../jsdocTemplateTagNameResolution.errors.txt | 16 +++ .../jsdocTemplateTagNameResolution.symbols | 15 +++ .../jsdocTemplateTagNameResolution.types | 18 ++++ .../jsdoc/jsdocTemplateTagDefault.ts | 76 ++++++++++++++ .../jsdoc/jsdocTemplateTagNameResolution.ts | 15 +++ 12 files changed, 462 insertions(+), 6 deletions(-) create mode 100644 tests/baselines/reference/jsdocTemplateTagDefault.errors.txt create mode 100644 tests/baselines/reference/jsdocTemplateTagDefault.symbols create mode 100644 tests/baselines/reference/jsdocTemplateTagDefault.types create mode 100644 tests/baselines/reference/jsdocTemplateTagNameResolution.errors.txt create mode 100644 tests/baselines/reference/jsdocTemplateTagNameResolution.symbols create mode 100644 tests/baselines/reference/jsdocTemplateTagNameResolution.types create mode 100644 tests/cases/conformance/jsdoc/jsdocTemplateTagDefault.ts create mode 100644 tests/cases/conformance/jsdoc/jsdocTemplateTagNameResolution.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index b8863458815ad..1387c5ef38276 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 38888e5e582ed..f1cb4e3d5b01d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2045,7 +2045,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`. @@ -12836,7 +12838,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); } @@ -33458,7 +33460,7 @@ namespace ts { } } - checkTypeParameters(node.typeParameters); + checkTypeParameters(getEffectiveTypeParameterDeclarations(node)); forEach(node.parameters, checkParameter); @@ -35089,6 +35091,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..10c6233d9a08e 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -8053,6 +8053,22 @@ namespace ts { return token() === SyntaxKind.OpenBraceToken ? parseJSDocTypeExpression() : undefined; } + function parseBracketNameInTemplateTag(): { name: Identifier, defaultType?: TypeNode } { + 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); + } + return { name, defaultType }; + } + function parseBracketNameInPropertyAndParamTag(): { name: EntityName, isBracketed: boolean } { // Looking for something like '[foo]', 'foo', '[foo.bar]' or 'foo.bar' const isBracketed = parseOptionalJsdoc(SyntaxKind.OpenBracketToken); @@ -8441,11 +8457,11 @@ namespace ts { function parseTemplateTagTypeParameter() { const typeParameterPos = getNodePos(); - const name = parseJSDocIdentifierName(Diagnostics.Unexpected_token_A_type_parameter_name_was_expected_without_curly_braces); + const { name, defaultType } = parseBracketNameInTemplateTag(); 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 00390b144b590..23b0fd67f33a6 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/jsdocTemplateTagDefault.errors.txt b/tests/baselines/reference/jsdocTemplateTagDefault.errors.txt new file mode 100644 index 0000000000000..7b3f3d1356936 --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateTagDefault.errors.txt @@ -0,0 +1,99 @@ +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(39,14): error TS2706: Required type parameters may not follow optional type parameters. +tests/cases/conformance/jsdoc/file.js(44,17): error TS2744: Type parameter defaults can only reference previously declared type parameters. +tests/cases/conformance/jsdoc/file.js(59,14): error TS2706: Required type parameters may not follow optional type parameters. +tests/cases/conformance/jsdoc/file.js(66,17): error TS2744: Type parameter defaults can only reference previously declared type parameters. + + +==== tests/cases/conformance/jsdoc/file.js (8 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 {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]} F + */ + + /** + * @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.symbols b/tests/baselines/reference/jsdocTemplateTagDefault.symbols new file mode 100644 index 0000000000000..c984d8e00163b --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateTagDefault.symbols @@ -0,0 +1,89 @@ +=== 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 {string | number} [T=string] + * @template U - error: Required type parameters cannot follow optional type parameters + * @typedef {[T, U]} F + */ + +/** + * @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, 54, 12)) +>b : Symbol(b, Decl(file.js, 54, 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, 54, 20)) +>a : Symbol(a, Decl(file.js, 62, 12)) +>b : Symbol(b, Decl(file.js, 62, 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, 62, 20)) +>a : Symbol(a, Decl(file.js, 70, 12)) +>b : Symbol(b, Decl(file.js, 70, 14)) + diff --git a/tests/baselines/reference/jsdocTemplateTagDefault.types b/tests/baselines/reference/jsdocTemplateTagDefault.types new file mode 100644 index 0000000000000..db806cc2b77d2 --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateTagDefault.types @@ -0,0 +1,97 @@ +=== 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 {string | number} [T=string] + * @template U - error: Required type parameters cannot follow optional type parameters + * @typedef {[T, U]} F + */ + +/** + * @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.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..8a532cde27895 --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocTemplateTagDefault.ts @@ -0,0 +1,76 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true +// @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 {string | number} [T=string] + * @template U - error: Required type parameters cannot follow optional type parameters + * @typedef {[T, U]} F + */ + +/** + * @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..e529a7be4250e --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocTemplateTagNameResolution.ts @@ -0,0 +1,15 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: 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 From 8460ea4361cf937f91877b0d5f0cfeb497656168 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 27 Aug 2021 16:32:58 -0700 Subject: [PATCH 2/4] Inline parseBracketNameInTemplateTag --- src/compiler/parser.ts | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 10c6233d9a08e..dbe5394908d48 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -8053,22 +8053,6 @@ namespace ts { return token() === SyntaxKind.OpenBraceToken ? parseJSDocTypeExpression() : undefined; } - function parseBracketNameInTemplateTag(): { name: Identifier, defaultType?: TypeNode } { - 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); - } - return { name, defaultType }; - } - function parseBracketNameInPropertyAndParamTag(): { name: EntityName, isBracketed: boolean } { // Looking for something like '[foo]', 'foo', '[foo.bar]' or 'foo.bar' const isBracketed = parseOptionalJsdoc(SyntaxKind.OpenBracketToken); @@ -8457,7 +8441,20 @@ namespace ts { function parseTemplateTagTypeParameter() { const typeParameterPos = getNodePos(); - const { name, defaultType } = parseBracketNameInTemplateTag(); + 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; } From 9131c0c0005394e4c235d8e3e473b767a5f19fc9 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 27 Aug 2021 17:54:50 -0700 Subject: [PATCH 3/4] Update baselines --- .../baselines/reference/completionEntryForUnionMethod.baseline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" } ] From 490e6813697317d9addde864a9999ec9424699bc Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 8 Sep 2021 15:55:37 -0700 Subject: [PATCH 4/4] Add js declaration emit tests --- .../jsdocTemplateTagDefault.errors.txt | 17 +- .../reference/jsdocTemplateTagDefault.js | 186 ++++++++++++++++++ .../reference/jsdocTemplateTagDefault.symbols | 22 +-- .../reference/jsdocTemplateTagDefault.types | 6 - .../jsdocTemplateTagNameResolution.js | 30 +++ .../jsdoc/jsdocTemplateTagDefault.ts | 9 +- .../jsdoc/jsdocTemplateTagNameResolution.ts | 3 +- 7 files changed, 232 insertions(+), 41 deletions(-) create mode 100644 tests/baselines/reference/jsdocTemplateTagDefault.js create mode 100644 tests/baselines/reference/jsdocTemplateTagNameResolution.js diff --git a/tests/baselines/reference/jsdocTemplateTagDefault.errors.txt b/tests/baselines/reference/jsdocTemplateTagDefault.errors.txt index 7b3f3d1356936..d21f546ed2dd4 100644 --- a/tests/baselines/reference/jsdocTemplateTagDefault.errors.txt +++ b/tests/baselines/reference/jsdocTemplateTagDefault.errors.txt @@ -2,13 +2,12 @@ tests/cases/conformance/jsdoc/file.js(9,20): error TS2322: Type 'number' is not 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(39,14): error TS2706: Required type parameters may not follow optional type parameters. -tests/cases/conformance/jsdoc/file.js(44,17): error TS2744: Type parameter defaults can only reference previously declared type parameters. -tests/cases/conformance/jsdoc/file.js(59,14): error TS2706: Required type parameters may not follow optional type parameters. -tests/cases/conformance/jsdoc/file.js(66,17): error TS2744: Type parameter defaults can only reference previously declared 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 (8 errors) ==== +==== tests/cases/conformance/jsdoc/file.js (7 errors) ==== /** * @template {string | number} [T=string] - ok: defaults are permitted * @typedef {[T]} A @@ -53,14 +52,6 @@ tests/cases/conformance/jsdoc/file.js(66,17): error TS2744: Type parameter defau * @typedef {[T, U]} E */ - /** - * @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]} F - */ - /** * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. ~ 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 index c984d8e00163b..19cba3a6a0474 100644 --- a/tests/baselines/reference/jsdocTemplateTagDefault.symbols +++ b/tests/baselines/reference/jsdocTemplateTagDefault.symbols @@ -42,12 +42,6 @@ const aNumber = [0]; * @typedef {[T, U]} E */ -/** - * @template {string | number} [T=string] - * @template U - error: Required type parameters cannot follow optional type parameters - * @typedef {[T, U]} F - */ - /** * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. * @template [U=T] @@ -62,8 +56,8 @@ const aNumber = [0]; */ function f1(a, b) {} >f1 : Symbol(f1, Decl(file.js, 12, 20)) ->a : Symbol(a, Decl(file.js, 54, 12)) ->b : Symbol(b, Decl(file.js, 54, 14)) +>a : Symbol(a, Decl(file.js, 48, 12)) +>b : Symbol(b, Decl(file.js, 48, 14)) /** * @template {string | number} [T=string] @@ -72,9 +66,9 @@ function f1(a, b) {} * @param {U} b */ function f2(a, b) {} ->f2 : Symbol(f2, Decl(file.js, 54, 20)) ->a : Symbol(a, Decl(file.js, 62, 12)) ->b : Symbol(b, Decl(file.js, 62, 14)) +>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. @@ -83,7 +77,7 @@ function f2(a, b) {} * @param {U} b */ function f3(a, b) {} ->f3 : Symbol(f3, Decl(file.js, 62, 20)) ->a : Symbol(a, Decl(file.js, 70, 12)) ->b : Symbol(b, Decl(file.js, 70, 14)) +>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 index db806cc2b77d2..7b00202e84268 100644 --- a/tests/baselines/reference/jsdocTemplateTagDefault.types +++ b/tests/baselines/reference/jsdocTemplateTagDefault.types @@ -50,12 +50,6 @@ const aNumber = [0]; * @typedef {[T, U]} E */ -/** - * @template {string | number} [T=string] - * @template U - error: Required type parameters cannot follow optional type parameters - * @typedef {[T, U]} F - */ - /** * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. * @template [U=T] 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/cases/conformance/jsdoc/jsdocTemplateTagDefault.ts b/tests/cases/conformance/jsdoc/jsdocTemplateTagDefault.ts index 8a532cde27895..c93359a7a6eb3 100644 --- a/tests/cases/conformance/jsdoc/jsdocTemplateTagDefault.ts +++ b/tests/cases/conformance/jsdoc/jsdocTemplateTagDefault.ts @@ -1,6 +1,7 @@ // @allowJs: true // @checkJs: true -// @noEmit: true +// @declaration: true +// @outDir: out // @Filename: file.js /** @@ -39,12 +40,6 @@ const aNumber = [0]; * @typedef {[T, U]} E */ -/** - * @template {string | number} [T=string] - * @template U - error: Required type parameters cannot follow optional type parameters - * @typedef {[T, U]} F - */ - /** * @template [T=U] - error: Type parameter defaults can only reference previously declared type parameters. * @template [U=T] diff --git a/tests/cases/conformance/jsdoc/jsdocTemplateTagNameResolution.ts b/tests/cases/conformance/jsdoc/jsdocTemplateTagNameResolution.ts index e529a7be4250e..a5b4d052ad2de 100644 --- a/tests/cases/conformance/jsdoc/jsdocTemplateTagNameResolution.ts +++ b/tests/cases/conformance/jsdoc/jsdocTemplateTagNameResolution.ts @@ -1,6 +1,7 @@ // @allowJs: true // @checkJs: true -// @noEmit: true +// @outDir: out +// @declaration: true // @Filename: file.js /**