diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2b255da7ae34d..e1c63442823dd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8128,6 +8128,10 @@ namespace ts { // TODO: GH#18217 (should the `|| assignedType` be at a lower precedence?) return (referenceType && assignedType ? getIntersectionType([assignedType, referenceType]) : referenceType || assignedType)!; } + const enumTag = getJSDocEnumTag(symbol.valueDeclaration); + if (enumTag && enumTag.typeExpression) { + return getTypeFromTypeNode(enumTag.typeExpression); + } } function getTypeReferenceTypeWorker(node: NodeWithTypeArguments, symbol: Symbol, typeArguments: Type[] | undefined): Type | undefined { @@ -16563,7 +16567,8 @@ namespace ts { const contextualTypeHasPattern = contextualType && contextualType.pattern && (contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern || contextualType.pattern.kind === SyntaxKind.ObjectLiteralExpression); const isInJSFile = isInJavaScriptFile(node) && !isInJsonFile(node); - const isJSObjectLiteral = !contextualType && isInJSFile; + const enumTag = getJSDocEnumTag(node); + const isJSObjectLiteral = !contextualType && isInJSFile && !enumTag; let typeFlags: TypeFlags = 0; let patternWithComputedProperties = false; let hasComputedStringProperty = false; @@ -16588,6 +16593,9 @@ namespace ts { checkTypeAssignableTo(type, jsDocType, memberDecl); type = jsDocType; } + else if (enumTag && enumTag.typeExpression) { + checkTypeAssignableTo(type, getTypeFromTypeNode(enumTag.typeExpression), memberDecl); + } } typeFlags |= type.flags; const nameType = computedNameType && computedNameType.flags & TypeFlags.StringOrNumberLiteralOrUnique ? @@ -16622,6 +16630,7 @@ namespace ts { symbolToString(member), typeToString(contextualType!)); } } + prop.declarations = member.declarations; prop.parent = member.parent; if (member.valueDeclaration) { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 4fd7f77978a45..1f52a1cc6e89f 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -491,6 +491,8 @@ namespace ts { visitNode(cbNode, (node as JSDocCallbackTag).typeExpression); case SyntaxKind.JSDocThisTag: return visitNode(cbNode, (node as JSDocThisTag).typeExpression); + case SyntaxKind.JSDocEnumTag: + return visitNode(cbNode, (node as JSDocEnumTag).typeExpression); case SyntaxKind.JSDocSignature: return visitNodes(cbNode, cbNodes, node.decorators) || visitNodes(cbNode, cbNodes, node.modifiers) || @@ -6527,6 +6529,9 @@ namespace ts { case "this": tag = parseThisTag(atToken, tagName); break; + case "enum": + tag = parseEnumTag(atToken, tagName); + break; case "arg": case "argument": case "param": @@ -6817,6 +6822,15 @@ namespace ts { return finishNode(tag); } + function parseEnumTag(atToken: AtToken, tagName: Identifier): JSDocEnumTag { + const tag = createNode(SyntaxKind.JSDocEnumTag, atToken.pos); + tag.atToken = atToken; + tag.tagName = tagName; + tag.typeExpression = parseJSDocTypeExpression(/*mayOmitBraces*/ true); + skipWhitespace(); + return finishNode(tag); + } + function parseTypedefTag(atToken: AtToken, tagName: Identifier, indent: number): JSDocTypedefTag { const typeExpression = tryParseTypeExpression(); skipWhitespace(); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 99d2291e89847..f8468203575c4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -372,6 +372,7 @@ namespace ts { JSDocAugmentsTag, JSDocClassTag, JSDocCallbackTag, + JSDocEnumTag, JSDocParameterTag, JSDocReturnTag, JSDocThisTag, @@ -581,6 +582,7 @@ namespace ts { | FunctionTypeNode | ConstructorTypeNode | JSDocFunctionType + | ExportDeclaration | EndOfFileToken; export type HasType = @@ -2202,7 +2204,7 @@ namespace ts { name: Identifier; } - export interface ExportDeclaration extends DeclarationStatement { + export interface ExportDeclaration extends DeclarationStatement, JSDocContainer { kind: SyntaxKind.ExportDeclaration; parent: SourceFile | ModuleBlock; /** Will not be assigned in the case of `export * from "foo";` */ @@ -2348,6 +2350,11 @@ namespace ts { kind: SyntaxKind.JSDocClassTag; } + export interface JSDocEnumTag extends JSDocTag { + kind: SyntaxKind.JSDocEnumTag; + typeExpression?: JSDocTypeExpression; + } + export interface JSDocThisTag extends JSDocTag { kind: SyntaxKind.JSDocThisTag; typeExpression?: JSDocTypeExpression; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 689e5cd2fef51..5c1ecac31b871 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4985,6 +4985,11 @@ namespace ts { return getFirstJSDocTag(node, isJSDocClassTag); } + /** Gets the JSDoc enum tag for the node if present */ + export function getJSDocEnumTag(node: Node): JSDocEnumTag | undefined { + return getFirstJSDocTag(node, isJSDocEnumTag); + } + /** Gets the JSDoc this tag for the node if present */ export function getJSDocThisTag(node: Node): JSDocThisTag | undefined { return getFirstJSDocTag(node, isJSDocThisTag); @@ -5749,6 +5754,10 @@ namespace ts { return node.kind === SyntaxKind.JSDocClassTag; } + export function isJSDocEnumTag(node: Node): node is JSDocEnumTag { + return node.kind === SyntaxKind.JSDocEnumTag; + } + export function isJSDocThisTag(node: Node): node is JSDocThisTag { return node.kind === SyntaxKind.JSDocThisTag; } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 99a9c3b610098..859bda59af1c6 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -368,20 +368,21 @@ declare namespace ts { JSDocAugmentsTag = 293, JSDocClassTag = 294, JSDocCallbackTag = 295, - JSDocParameterTag = 296, - JSDocReturnTag = 297, - JSDocThisTag = 298, - JSDocTypeTag = 299, - JSDocTemplateTag = 300, - JSDocTypedefTag = 301, - JSDocPropertyTag = 302, - SyntaxList = 303, - NotEmittedStatement = 304, - PartiallyEmittedExpression = 305, - CommaListExpression = 306, - MergeDeclarationMarker = 307, - EndOfDeclarationMarker = 308, - Count = 309, + JSDocEnumTag = 296, + JSDocParameterTag = 297, + JSDocReturnTag = 298, + JSDocThisTag = 299, + JSDocTypeTag = 300, + JSDocTemplateTag = 301, + JSDocTypedefTag = 302, + JSDocPropertyTag = 303, + SyntaxList = 304, + NotEmittedStatement = 305, + PartiallyEmittedExpression = 306, + CommaListExpression = 307, + MergeDeclarationMarker = 308, + EndOfDeclarationMarker = 309, + Count = 310, FirstAssignment = 58, LastAssignment = 70, FirstCompoundAssignment = 59, @@ -408,9 +409,9 @@ declare namespace ts { LastBinaryOperator = 70, FirstNode = 146, FirstJSDocNode = 281, - LastJSDocNode = 302, + LastJSDocNode = 303, FirstJSDocTagNode = 292, - LastJSDocTagNode = 302 + LastJSDocTagNode = 303 } enum NodeFlags { None = 0, @@ -479,7 +480,7 @@ declare namespace ts { } interface JSDocContainer { } - type HasJSDoc = ParameterDeclaration | CallSignatureDeclaration | ConstructSignatureDeclaration | MethodSignature | PropertySignature | ArrowFunction | ParenthesizedExpression | SpreadAssignment | ShorthandPropertyAssignment | PropertyAssignment | FunctionExpression | LabeledStatement | ExpressionStatement | VariableStatement | FunctionDeclaration | ConstructorDeclaration | MethodDeclaration | PropertyDeclaration | AccessorDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumMember | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | JSDocFunctionType | EndOfFileToken; + type HasJSDoc = ParameterDeclaration | CallSignatureDeclaration | ConstructSignatureDeclaration | MethodSignature | PropertySignature | ArrowFunction | ParenthesizedExpression | SpreadAssignment | ShorthandPropertyAssignment | PropertyAssignment | FunctionExpression | LabeledStatement | ExpressionStatement | VariableStatement | FunctionDeclaration | ConstructorDeclaration | MethodDeclaration | PropertyDeclaration | AccessorDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumMember | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | JSDocFunctionType | ExportDeclaration | EndOfFileToken; type HasType = SignatureDeclaration | VariableDeclaration | ParameterDeclaration | PropertySignature | PropertyDeclaration | TypePredicateNode | ParenthesizedTypeNode | TypeOperatorNode | MappedTypeNode | AssertionExpression | TypeAliasDeclaration | JSDocTypeExpression | JSDocNonNullableType | JSDocNullableType | JSDocOptionalType | JSDocVariadicType; type HasInitializer = HasExpressionInitializer | ForStatement | ForInStatement | ForOfStatement | JsxAttribute; type HasExpressionInitializer = VariableDeclaration | ParameterDeclaration | BindingElement | PropertySignature | PropertyDeclaration | PropertyAssignment | EnumMember; @@ -1437,7 +1438,7 @@ declare namespace ts { kind: SyntaxKind.NamespaceExportDeclaration; name: Identifier; } - interface ExportDeclaration extends DeclarationStatement { + interface ExportDeclaration extends DeclarationStatement, JSDocContainer { kind: SyntaxKind.ExportDeclaration; parent: SourceFile | ModuleBlock; /** Will not be assigned in the case of `export * from "foo";` */ @@ -1556,6 +1557,10 @@ declare namespace ts { interface JSDocClassTag extends JSDocTag { kind: SyntaxKind.JSDocClassTag; } + interface JSDocEnumTag extends JSDocTag { + kind: SyntaxKind.JSDocEnumTag; + typeExpression?: JSDocTypeExpression; + } interface JSDocThisTag extends JSDocTag { kind: SyntaxKind.JSDocThisTag; typeExpression?: JSDocTypeExpression; @@ -3220,6 +3225,8 @@ declare namespace ts { function getJSDocAugmentsTag(node: Node): JSDocAugmentsTag | undefined; /** Gets the JSDoc class tag for the node if present */ function getJSDocClassTag(node: Node): JSDocClassTag | undefined; + /** Gets the JSDoc enum tag for the node if present */ + function getJSDocEnumTag(node: Node): JSDocEnumTag | undefined; /** Gets the JSDoc this tag for the node if present */ function getJSDocThisTag(node: Node): JSDocThisTag | undefined; /** Gets the JSDoc return tag for the node if present */ @@ -3412,6 +3419,7 @@ declare namespace ts { function isJSDoc(node: Node): node is JSDoc; function isJSDocAugmentsTag(node: Node): node is JSDocAugmentsTag; function isJSDocClassTag(node: Node): node is JSDocClassTag; + function isJSDocEnumTag(node: Node): node is JSDocEnumTag; function isJSDocThisTag(node: Node): node is JSDocThisTag; function isJSDocParameterTag(node: Node): node is JSDocParameterTag; function isJSDocReturnTag(node: Node): node is JSDocReturnTag; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 41510d5192bda..31a7c8abbabaf 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -368,20 +368,21 @@ declare namespace ts { JSDocAugmentsTag = 293, JSDocClassTag = 294, JSDocCallbackTag = 295, - JSDocParameterTag = 296, - JSDocReturnTag = 297, - JSDocThisTag = 298, - JSDocTypeTag = 299, - JSDocTemplateTag = 300, - JSDocTypedefTag = 301, - JSDocPropertyTag = 302, - SyntaxList = 303, - NotEmittedStatement = 304, - PartiallyEmittedExpression = 305, - CommaListExpression = 306, - MergeDeclarationMarker = 307, - EndOfDeclarationMarker = 308, - Count = 309, + JSDocEnumTag = 296, + JSDocParameterTag = 297, + JSDocReturnTag = 298, + JSDocThisTag = 299, + JSDocTypeTag = 300, + JSDocTemplateTag = 301, + JSDocTypedefTag = 302, + JSDocPropertyTag = 303, + SyntaxList = 304, + NotEmittedStatement = 305, + PartiallyEmittedExpression = 306, + CommaListExpression = 307, + MergeDeclarationMarker = 308, + EndOfDeclarationMarker = 309, + Count = 310, FirstAssignment = 58, LastAssignment = 70, FirstCompoundAssignment = 59, @@ -408,9 +409,9 @@ declare namespace ts { LastBinaryOperator = 70, FirstNode = 146, FirstJSDocNode = 281, - LastJSDocNode = 302, + LastJSDocNode = 303, FirstJSDocTagNode = 292, - LastJSDocTagNode = 302 + LastJSDocTagNode = 303 } enum NodeFlags { None = 0, @@ -479,7 +480,7 @@ declare namespace ts { } interface JSDocContainer { } - type HasJSDoc = ParameterDeclaration | CallSignatureDeclaration | ConstructSignatureDeclaration | MethodSignature | PropertySignature | ArrowFunction | ParenthesizedExpression | SpreadAssignment | ShorthandPropertyAssignment | PropertyAssignment | FunctionExpression | LabeledStatement | ExpressionStatement | VariableStatement | FunctionDeclaration | ConstructorDeclaration | MethodDeclaration | PropertyDeclaration | AccessorDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumMember | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | JSDocFunctionType | EndOfFileToken; + type HasJSDoc = ParameterDeclaration | CallSignatureDeclaration | ConstructSignatureDeclaration | MethodSignature | PropertySignature | ArrowFunction | ParenthesizedExpression | SpreadAssignment | ShorthandPropertyAssignment | PropertyAssignment | FunctionExpression | LabeledStatement | ExpressionStatement | VariableStatement | FunctionDeclaration | ConstructorDeclaration | MethodDeclaration | PropertyDeclaration | AccessorDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumMember | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | JSDocFunctionType | ExportDeclaration | EndOfFileToken; type HasType = SignatureDeclaration | VariableDeclaration | ParameterDeclaration | PropertySignature | PropertyDeclaration | TypePredicateNode | ParenthesizedTypeNode | TypeOperatorNode | MappedTypeNode | AssertionExpression | TypeAliasDeclaration | JSDocTypeExpression | JSDocNonNullableType | JSDocNullableType | JSDocOptionalType | JSDocVariadicType; type HasInitializer = HasExpressionInitializer | ForStatement | ForInStatement | ForOfStatement | JsxAttribute; type HasExpressionInitializer = VariableDeclaration | ParameterDeclaration | BindingElement | PropertySignature | PropertyDeclaration | PropertyAssignment | EnumMember; @@ -1437,7 +1438,7 @@ declare namespace ts { kind: SyntaxKind.NamespaceExportDeclaration; name: Identifier; } - interface ExportDeclaration extends DeclarationStatement { + interface ExportDeclaration extends DeclarationStatement, JSDocContainer { kind: SyntaxKind.ExportDeclaration; parent: SourceFile | ModuleBlock; /** Will not be assigned in the case of `export * from "foo";` */ @@ -1556,6 +1557,10 @@ declare namespace ts { interface JSDocClassTag extends JSDocTag { kind: SyntaxKind.JSDocClassTag; } + interface JSDocEnumTag extends JSDocTag { + kind: SyntaxKind.JSDocEnumTag; + typeExpression?: JSDocTypeExpression; + } interface JSDocThisTag extends JSDocTag { kind: SyntaxKind.JSDocThisTag; typeExpression?: JSDocTypeExpression; @@ -3220,6 +3225,8 @@ declare namespace ts { function getJSDocAugmentsTag(node: Node): JSDocAugmentsTag | undefined; /** Gets the JSDoc class tag for the node if present */ function getJSDocClassTag(node: Node): JSDocClassTag | undefined; + /** Gets the JSDoc enum tag for the node if present */ + function getJSDocEnumTag(node: Node): JSDocEnumTag | undefined; /** Gets the JSDoc this tag for the node if present */ function getJSDocThisTag(node: Node): JSDocThisTag | undefined; /** Gets the JSDoc return tag for the node if present */ @@ -3412,6 +3419,7 @@ declare namespace ts { function isJSDoc(node: Node): node is JSDoc; function isJSDocAugmentsTag(node: Node): node is JSDocAugmentsTag; function isJSDocClassTag(node: Node): node is JSDocClassTag; + function isJSDocEnumTag(node: Node): node is JSDocEnumTag; function isJSDocThisTag(node: Node): node is JSDocThisTag; function isJSDocParameterTag(node: Node): node is JSDocParameterTag; function isJSDocReturnTag(node: Node): node is JSDocReturnTag; diff --git a/tests/baselines/reference/enumTag.errors.txt b/tests/baselines/reference/enumTag.errors.txt new file mode 100644 index 0000000000000..0c2524a1dc1a9 --- /dev/null +++ b/tests/baselines/reference/enumTag.errors.txt @@ -0,0 +1,65 @@ +tests/cases/conformance/jsdoc/a.js(6,5): error TS2322: Type 'number' is not assignable to type 'string'. +tests/cases/conformance/jsdoc/a.js(12,5): error TS2322: Type 'string' is not assignable to type 'number'. +tests/cases/conformance/jsdoc/a.js(37,16): error TS2339: Property 'UNKNOWN' does not exist on type '{ START: string; MIDDLE: string; END: string; MISTAKE: number; OK_I_GUESS: number; }'. + + +==== tests/cases/conformance/jsdoc/a.js (3 errors) ==== + /** @enum {string} */ + const Target = { + START: "start", + MIDDLE: "middle", + END: "end", + MISTAKE: 1, + ~~~~~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'string'. + /** @type {number} */ + OK_I_GUESS: 2 + } + /** @enum {number} */ + const Second = { + MISTAKE: "end", + ~~~~~~~~~~~~~~ +!!! error TS2322: Type 'string' is not assignable to type 'number'. + OK: 1, + /** @type {number} */ + FINE: 2, + } + /** @enum {function(number): number} */ + const Fs = { + ADD1: n => n + 1, + ID: n => n, + SUB1: n => n - 1 + } + + /** @param {Target} t + * @param {Second} s + * @param {Fs} f + */ + function consume(t,s,f) { + /** @type {string} */ + var str = t + /** @type {number} */ + var num = s + /** @type {(n: number) => number} */ + var fun = f + /** @type {Target} */ + var v = Target.START + v = Target.UNKNOWN // error, can't find 'UNKNOWN' + ~~~~~~~ +!!! error TS2339: Property 'UNKNOWN' does not exist on type '{ START: string; MIDDLE: string; END: string; MISTAKE: number; OK_I_GUESS: number; }'. + v = Second.MISTAKE // meh..ok, I guess? + v = 'something else' // allowed, like Typescript's classic enums and unlike its string enums + } + /** @param {string} s */ + function ff(s) { + // element access with arbitrary string is an error only with noImplicitAny + if (!Target[s]) { + return null + } + else { + return Target[s] + } + } + + + \ No newline at end of file diff --git a/tests/baselines/reference/enumTag.symbols b/tests/baselines/reference/enumTag.symbols new file mode 100644 index 0000000000000..ed0c11522f427 --- /dev/null +++ b/tests/baselines/reference/enumTag.symbols @@ -0,0 +1,121 @@ +=== tests/cases/conformance/jsdoc/a.js === +/** @enum {string} */ +const Target = { +>Target : Symbol(Target, Decl(a.js, 1, 5)) + + START: "start", +>START : Symbol(START, Decl(a.js, 1, 16)) + + MIDDLE: "middle", +>MIDDLE : Symbol(MIDDLE, Decl(a.js, 2, 19)) + + END: "end", +>END : Symbol(END, Decl(a.js, 3, 21)) + + MISTAKE: 1, +>MISTAKE : Symbol(MISTAKE, Decl(a.js, 4, 15)) + + /** @type {number} */ + OK_I_GUESS: 2 +>OK_I_GUESS : Symbol(OK_I_GUESS, Decl(a.js, 5, 15)) +} +/** @enum {number} */ +const Second = { +>Second : Symbol(Second, Decl(a.js, 10, 5)) + + MISTAKE: "end", +>MISTAKE : Symbol(MISTAKE, Decl(a.js, 10, 16)) + + OK: 1, +>OK : Symbol(OK, Decl(a.js, 11, 19)) + + /** @type {number} */ + FINE: 2, +>FINE : Symbol(FINE, Decl(a.js, 12, 10)) +} +/** @enum {function(number): number} */ +const Fs = { +>Fs : Symbol(Fs, Decl(a.js, 17, 5)) + + ADD1: n => n + 1, +>ADD1 : Symbol(ADD1, Decl(a.js, 17, 12)) +>n : Symbol(n, Decl(a.js, 18, 9)) +>n : Symbol(n, Decl(a.js, 18, 9)) + + ID: n => n, +>ID : Symbol(ID, Decl(a.js, 18, 21)) +>n : Symbol(n, Decl(a.js, 19, 7)) +>n : Symbol(n, Decl(a.js, 19, 7)) + + SUB1: n => n - 1 +>SUB1 : Symbol(SUB1, Decl(a.js, 19, 15)) +>n : Symbol(n, Decl(a.js, 20, 9)) +>n : Symbol(n, Decl(a.js, 20, 9)) +} + +/** @param {Target} t + * @param {Second} s + * @param {Fs} f + */ +function consume(t,s,f) { +>consume : Symbol(consume, Decl(a.js, 21, 1)) +>t : Symbol(t, Decl(a.js, 27, 17)) +>s : Symbol(s, Decl(a.js, 27, 19)) +>f : Symbol(f, Decl(a.js, 27, 21)) + + /** @type {string} */ + var str = t +>str : Symbol(str, Decl(a.js, 29, 7)) +>t : Symbol(t, Decl(a.js, 27, 17)) + + /** @type {number} */ + var num = s +>num : Symbol(num, Decl(a.js, 31, 7)) +>s : Symbol(s, Decl(a.js, 27, 19)) + + /** @type {(n: number) => number} */ + var fun = f +>fun : Symbol(fun, Decl(a.js, 33, 7)) +>f : Symbol(f, Decl(a.js, 27, 21)) + + /** @type {Target} */ + var v = Target.START +>v : Symbol(v, Decl(a.js, 35, 7)) +>Target.START : Symbol(START, Decl(a.js, 1, 16)) +>Target : Symbol(Target, Decl(a.js, 1, 5)) +>START : Symbol(START, Decl(a.js, 1, 16)) + + v = Target.UNKNOWN // error, can't find 'UNKNOWN' +>v : Symbol(v, Decl(a.js, 35, 7)) +>Target : Symbol(Target, Decl(a.js, 1, 5)) + + v = Second.MISTAKE // meh..ok, I guess? +>v : Symbol(v, Decl(a.js, 35, 7)) +>Second.MISTAKE : Symbol(MISTAKE, Decl(a.js, 10, 16)) +>Second : Symbol(Second, Decl(a.js, 10, 5)) +>MISTAKE : Symbol(MISTAKE, Decl(a.js, 10, 16)) + + v = 'something else' // allowed, like Typescript's classic enums and unlike its string enums +>v : Symbol(v, Decl(a.js, 35, 7)) +} +/** @param {string} s */ +function ff(s) { +>ff : Symbol(ff, Decl(a.js, 39, 1)) +>s : Symbol(s, Decl(a.js, 41, 12)) + + // element access with arbitrary string is an error only with noImplicitAny + if (!Target[s]) { +>Target : Symbol(Target, Decl(a.js, 1, 5)) +>s : Symbol(s, Decl(a.js, 41, 12)) + + return null + } + else { + return Target[s] +>Target : Symbol(Target, Decl(a.js, 1, 5)) +>s : Symbol(s, Decl(a.js, 41, 12)) + } +} + + + diff --git a/tests/baselines/reference/enumTag.types b/tests/baselines/reference/enumTag.types new file mode 100644 index 0000000000000..fa8e537b6f583 --- /dev/null +++ b/tests/baselines/reference/enumTag.types @@ -0,0 +1,149 @@ +=== tests/cases/conformance/jsdoc/a.js === +/** @enum {string} */ +const Target = { +>Target : { START: string; MIDDLE: string; END: string; MISTAKE: number; OK_I_GUESS: number; } +>{ START: "start", MIDDLE: "middle", END: "end", MISTAKE: 1, /** @type {number} */ OK_I_GUESS: 2} : { START: string; MIDDLE: string; END: string; MISTAKE: number; OK_I_GUESS: number; } + + START: "start", +>START : string +>"start" : "start" + + MIDDLE: "middle", +>MIDDLE : string +>"middle" : "middle" + + END: "end", +>END : string +>"end" : "end" + + MISTAKE: 1, +>MISTAKE : number +>1 : 1 + + /** @type {number} */ + OK_I_GUESS: 2 +>OK_I_GUESS : number +>2 : 2 +} +/** @enum {number} */ +const Second = { +>Second : { MISTAKE: string; OK: number; FINE: number; } +>{ MISTAKE: "end", OK: 1, /** @type {number} */ FINE: 2,} : { MISTAKE: string; OK: number; FINE: number; } + + MISTAKE: "end", +>MISTAKE : string +>"end" : "end" + + OK: 1, +>OK : number +>1 : 1 + + /** @type {number} */ + FINE: 2, +>FINE : number +>2 : 2 +} +/** @enum {function(number): number} */ +const Fs = { +>Fs : { ADD1: (n: any) => any; ID: (n: any) => any; SUB1: (n: any) => number; } +>{ ADD1: n => n + 1, ID: n => n, SUB1: n => n - 1} : { ADD1: (n: any) => any; ID: (n: any) => any; SUB1: (n: any) => number; } + + ADD1: n => n + 1, +>ADD1 : (n: any) => any +>n => n + 1 : (n: any) => any +>n : any +>n + 1 : any +>n : any +>1 : 1 + + ID: n => n, +>ID : (n: any) => any +>n => n : (n: any) => any +>n : any +>n : any + + SUB1: n => n - 1 +>SUB1 : (n: any) => number +>n => n - 1 : (n: any) => number +>n : any +>n - 1 : number +>n : any +>1 : 1 +} + +/** @param {Target} t + * @param {Second} s + * @param {Fs} f + */ +function consume(t,s,f) { +>consume : (t: string, s: number, f: (arg0: number) => number) => void +>t : string +>s : number +>f : (arg0: number) => number + + /** @type {string} */ + var str = t +>str : string +>t : string + + /** @type {number} */ + var num = s +>num : number +>s : number + + /** @type {(n: number) => number} */ + var fun = f +>fun : (n: number) => number +>f : (arg0: number) => number + + /** @type {Target} */ + var v = Target.START +>v : string +>Target.START : string +>Target : { START: string; MIDDLE: string; END: string; MISTAKE: number; OK_I_GUESS: number; } +>START : string + + v = Target.UNKNOWN // error, can't find 'UNKNOWN' +>v = Target.UNKNOWN : any +>v : string +>Target.UNKNOWN : any +>Target : { START: string; MIDDLE: string; END: string; MISTAKE: number; OK_I_GUESS: number; } +>UNKNOWN : any + + v = Second.MISTAKE // meh..ok, I guess? +>v = Second.MISTAKE : string +>v : string +>Second.MISTAKE : string +>Second : { MISTAKE: string; OK: number; FINE: number; } +>MISTAKE : string + + v = 'something else' // allowed, like Typescript's classic enums and unlike its string enums +>v = 'something else' : "something else" +>v : string +>'something else' : "something else" +} +/** @param {string} s */ +function ff(s) { +>ff : (s: string) => any +>s : string + + // element access with arbitrary string is an error only with noImplicitAny + if (!Target[s]) { +>!Target[s] : boolean +>Target[s] : any +>Target : { START: string; MIDDLE: string; END: string; MISTAKE: number; OK_I_GUESS: number; } +>s : string + + return null +>null : null + } + else { + return Target[s] +>Target[s] : any +>Target : { START: string; MIDDLE: string; END: string; MISTAKE: number; OK_I_GUESS: number; } +>s : string + } +} + + + diff --git a/tests/cases/conformance/jsdoc/enumTag.ts b/tests/cases/conformance/jsdoc/enumTag.ts new file mode 100644 index 0000000000000..bd740d879a18c --- /dev/null +++ b/tests/cases/conformance/jsdoc/enumTag.ts @@ -0,0 +1,56 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true +// @Filename: a.js +/** @enum {string} */ +const Target = { + START: "start", + MIDDLE: "middle", + END: "end", + MISTAKE: 1, + /** @type {number} */ + OK_I_GUESS: 2 +} +/** @enum {number} */ +const Second = { + MISTAKE: "end", + OK: 1, + /** @type {number} */ + FINE: 2, +} +/** @enum {function(number): number} */ +const Fs = { + ADD1: n => n + 1, + ID: n => n, + SUB1: n => n - 1 +} + +/** @param {Target} t + * @param {Second} s + * @param {Fs} f + */ +function consume(t,s,f) { + /** @type {string} */ + var str = t + /** @type {number} */ + var num = s + /** @type {(n: number) => number} */ + var fun = f + /** @type {Target} */ + var v = Target.START + v = Target.UNKNOWN // error, can't find 'UNKNOWN' + v = Second.MISTAKE // meh..ok, I guess? + v = 'something else' // allowed, like Typescript's classic enums and unlike its string enums +} +/** @param {string} s */ +function ff(s) { + // element access with arbitrary string is an error only with noImplicitAny + if (!Target[s]) { + return null + } + else { + return Target[s] + } +} + +