diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0599eb4bbbc60..ddc188ace9209 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5529,6 +5529,8 @@ namespace ts { return getTypeFromThisTypeNode(node); case SyntaxKind.LiteralType: return getTypeFromLiteralTypeNode(node); + case SyntaxKind.JSDocLiteralType: + return getTypeFromLiteralTypeNode((node).literal); case SyntaxKind.TypeReference: case SyntaxKind.JSDocTypeReference: return getTypeFromTypeReference(node); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 014d092621368..07f013e4f318e 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -417,6 +417,8 @@ namespace ts { case SyntaxKind.JSDocPropertyTag: return visitNode(cbNode, (node).typeExpression) || visitNode(cbNode, (node).name); + case SyntaxKind.JSDocLiteralType: + return visitNode(cbNode, (node).literal); } } @@ -5889,9 +5891,13 @@ namespace ts { case SyntaxKind.SymbolKeyword: case SyntaxKind.VoidKeyword: return parseTokenNode(); + case SyntaxKind.StringLiteral: + case SyntaxKind.NumericLiteral: + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + return parseJSDocLiteralType(); } - // TODO (drosen): Parse string literal types in JSDoc as well. return parseJSDocTypeReference(); } @@ -6070,6 +6076,12 @@ namespace ts { return finishNode(result); } + function parseJSDocLiteralType(): JSDocLiteralType { + const result = createNode(SyntaxKind.JSDocLiteralType); + result.literal = parseLiteralTypeNode(); + return finishNode(result); + } + function parseJSDocUnknownOrNullableType(): JSDocUnknownType | JSDocNullableType { const pos = scanner.getStartPos(); // skip the ? diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 28ededebb0de1..45187b784b020 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -346,6 +346,7 @@ namespace ts { JSDocTypedefTag, JSDocPropertyTag, JSDocTypeLiteral, + JSDocLiteralType, // Synthesized list SyntaxList, @@ -376,9 +377,9 @@ namespace ts { LastBinaryOperator = CaretEqualsToken, FirstNode = QualifiedName, FirstJSDocNode = JSDocTypeExpression, - LastJSDocNode = JSDocTypeLiteral, + LastJSDocNode = JSDocLiteralType, FirstJSDocTagNode = JSDocComment, - LastJSDocTagNode = JSDocTypeLiteral + LastJSDocTagNode = JSDocLiteralType } export const enum NodeFlags { @@ -1492,6 +1493,10 @@ namespace ts { type: JSDocType; } + export interface JSDocLiteralType extends JSDocType { + literal: LiteralTypeNode; + } + export type JSDocTypeReferencingNode = JSDocThisType | JSDocConstructorType | JSDocVariadicType | JSDocOptionalType | JSDocNullableType | JSDocNonNullableType; // @kind(SyntaxKind.JSDocRecordMember) diff --git a/tests/baselines/reference/jsdocLiteral.js b/tests/baselines/reference/jsdocLiteral.js new file mode 100644 index 0000000000000..1a017160302c9 --- /dev/null +++ b/tests/baselines/reference/jsdocLiteral.js @@ -0,0 +1,24 @@ +//// [in.js] +/** + * @param {'literal'} p1 + * @param {"literal"} p2 + * @param {'literal' | 'other'} p3 + * @param {'literal' | number} p4 + * @param {12 | true | 'str'} p5 + */ +function f(p1, p2, p3, p4, p5) { + return p1 + p2 + p3 + p4 + p5 + '.'; +} + + +//// [out.js] +/** + * @param {'literal'} p1 + * @param {"literal"} p2 + * @param {'literal' | 'other'} p3 + * @param {'literal' | number} p4 + * @param {12 | true | 'str'} p5 + */ +function f(p1, p2, p3, p4, p5) { + return p1 + p2 + p3 + p4 + p5 + '.'; +} diff --git a/tests/baselines/reference/jsdocLiteral.symbols b/tests/baselines/reference/jsdocLiteral.symbols new file mode 100644 index 0000000000000..9f01fe782aa62 --- /dev/null +++ b/tests/baselines/reference/jsdocLiteral.symbols @@ -0,0 +1,24 @@ +=== tests/cases/conformance/jsdoc/in.js === +/** + * @param {'literal'} p1 + * @param {"literal"} p2 + * @param {'literal' | 'other'} p3 + * @param {'literal' | number} p4 + * @param {12 | true | 'str'} p5 + */ +function f(p1, p2, p3, p4, p5) { +>f : Symbol(f, Decl(in.js, 0, 0)) +>p1 : Symbol(p1, Decl(in.js, 7, 11)) +>p2 : Symbol(p2, Decl(in.js, 7, 14)) +>p3 : Symbol(p3, Decl(in.js, 7, 18)) +>p4 : Symbol(p4, Decl(in.js, 7, 22)) +>p5 : Symbol(p5, Decl(in.js, 7, 26)) + + return p1 + p2 + p3 + p4 + p5 + '.'; +>p1 : Symbol(p1, Decl(in.js, 7, 11)) +>p2 : Symbol(p2, Decl(in.js, 7, 14)) +>p3 : Symbol(p3, Decl(in.js, 7, 18)) +>p4 : Symbol(p4, Decl(in.js, 7, 22)) +>p5 : Symbol(p5, Decl(in.js, 7, 26)) +} + diff --git a/tests/baselines/reference/jsdocLiteral.types b/tests/baselines/reference/jsdocLiteral.types new file mode 100644 index 0000000000000..b6c9e522ac35c --- /dev/null +++ b/tests/baselines/reference/jsdocLiteral.types @@ -0,0 +1,30 @@ +=== tests/cases/conformance/jsdoc/in.js === +/** + * @param {'literal'} p1 + * @param {"literal"} p2 + * @param {'literal' | 'other'} p3 + * @param {'literal' | number} p4 + * @param {12 | true | 'str'} p5 + */ +function f(p1, p2, p3, p4, p5) { +>f : (p1: "literal", p2: "literal", p3: "literal" | "other", p4: number | "literal", p5: true | 12 | "str") => string +>p1 : "literal" +>p2 : "literal" +>p3 : "literal" | "other" +>p4 : number | "literal" +>p5 : true | 12 | "str" + + return p1 + p2 + p3 + p4 + p5 + '.'; +>p1 + p2 + p3 + p4 + p5 + '.' : string +>p1 + p2 + p3 + p4 + p5 : string +>p1 + p2 + p3 + p4 : string +>p1 + p2 + p3 : string +>p1 + p2 : string +>p1 : "literal" +>p2 : "literal" +>p3 : "literal" | "other" +>p4 : number | "literal" +>p5 : true | 12 | "str" +>'.' : string +} + diff --git a/tests/cases/conformance/jsdoc/jsdocLiteral.ts b/tests/cases/conformance/jsdoc/jsdocLiteral.ts new file mode 100644 index 0000000000000..bd0d2d562f9a5 --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocLiteral.ts @@ -0,0 +1,13 @@ +// @allowJs: true +// @filename: in.js +// @out: out.js +/** + * @param {'literal'} p1 + * @param {"literal"} p2 + * @param {'literal' | 'other'} p3 + * @param {'literal' | number} p4 + * @param {12 | true | 'str'} p5 + */ +function f(p1, p2, p3, p4, p5) { + return p1 + p2 + p3 + p4 + p5 + '.'; +} diff --git a/tests/cases/fourslash/completionForStringLiteral4.ts b/tests/cases/fourslash/completionForStringLiteral4.ts new file mode 100644 index 0000000000000..11ae699eab8d8 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteral4.ts @@ -0,0 +1,23 @@ +/// +// @allowJs: true +// @Filename: in.js +/////** I am documentation +//// * @param {'literal'} p1 +//// * @param {"literal"} p2 +//// * @param {'other1' | 'other2'} p3 +//// * @param {'literal' | number} p4 +//// * @param {12 | true} p5 +//// */ +////function f(p1, p2, p3, p4, p5) { +//// return p1 + p2 + p3 + p4 + p5 + '.'; +////} +////f/*1*/('literal', 'literal', "o/*2*/ther1", 12); + +goTo.marker('1'); +verify.quickInfoExists(); +verify.quickInfoIs('function f(p1: "literal", p2: "literal", p3: "other1" | "other2", p4: number | "literal", p5: true | 12): string', 'I am documentation'); + +goTo.marker('2'); +verify.completionListContains("other1"); +verify.completionListContains("other2"); +verify.memberListCount(2);