Skip to content

Commit

Permalink
Fix 'as const'-like behavior in JSDoc type cast
Browse files Browse the repository at this point in the history
  • Loading branch information
rbuckton committed Aug 16, 2021
1 parent d50c91d commit 7b70f43
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 7 deletions.
4 changes: 3 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25858,7 +25858,9 @@ namespace ts {
case SyntaxKind.ParenthesizedExpression: {
// Like in `checkParenthesizedExpression`, an `/** @type {xyz} */` comment before a parenthesized expression acts as a type cast.
const tag = isInJSFile(parent) ? getJSDocTypeTag(parent) : undefined;
return tag ? getTypeFromTypeNode(tag.typeExpression.type) : getContextualType(parent as ParenthesizedExpression, contextFlags);
return !tag ? getContextualType(parent as ParenthesizedExpression, contextFlags) :
isJSDocTypeTag(tag) && isConstTypeReference(tag.typeExpression.type) ? tryFindWhenConstTypeReference(parent as ParenthesizedExpression) :
getTypeFromTypeNode(tag.typeExpression.type);
}
case SyntaxKind.NonNullExpression:
return getContextualType(parent as NonNullExpression, contextFlags);
Expand Down
24 changes: 22 additions & 2 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2634,13 +2634,13 @@ namespace ts {
let result: (JSDoc | JSDocTag)[] | undefined;
// Pull parameter comments from declaring function as well
if (isVariableLike(hostNode) && hasInitializer(hostNode) && hasJSDocNodes(hostNode.initializer!)) {
result = append(result, last((hostNode.initializer as HasJSDoc).jsDoc!));
result = addRange(result, filterOwnedJSDocTags(hostNode, last((hostNode.initializer as HasJSDoc).jsDoc!)));
}

let node: Node | undefined = hostNode;
while (node && node.parent) {
if (hasJSDocNodes(node)) {
result = append(result, last(node.jsDoc!));
result = addRange(result, filterOwnedJSDocTags(hostNode, last(node.jsDoc!)));
}

if (node.kind === SyntaxKind.Parameter) {
Expand All @@ -2656,6 +2656,26 @@ namespace ts {
return result || emptyArray;
}

function filterOwnedJSDocTags(hostNode: Node, jsDoc: JSDoc | JSDocTag) {
if (isJSDoc(jsDoc)) {
const ownedTags = filter(jsDoc.tags, tag => ownsJSDocTag(hostNode, tag));
return jsDoc.tags === ownedTags ? [jsDoc] : ownedTags;
}
return ownsJSDocTag(hostNode, jsDoc) ? [jsDoc] : undefined;
}

/**
* Determines whether a host node owns a jsDoc tag. A `@type` tag attached to a
* a ParenthesizedExpression belongs only to the ParenthesizedExpression.
*/
function ownsJSDocTag(hostNode: Node, tag: JSDocTag) {
return !isJSDocTypeTag(tag)
|| !tag.parent
|| !isJSDoc(tag.parent)
|| !isParenthesizedExpression(tag.parent.parent)
|| tag.parent.parent === hostNode;
}

export function getNextJSDocCommentLocation(node: Node) {
const parent = node.parent;
if (parent.kind === SyntaxKind.PropertyAssignment ||
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/importTypeInJSDoc.types
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ export = MyClass;
*/

let a = /** @type {Foo} */(/** @type {*} */(undefined));
>a : import("tests/cases/conformance/types/import/externs")
>a : any
>(/** @type {*} */(undefined)) : import("tests/cases/conformance/types/import/externs")
>(undefined) : any
>undefined : undefined

a = new Foo({doer: Foo.Bar});
>a = new Foo({doer: Foo.Bar}) : import("tests/cases/conformance/types/import/externs")
>a : import("tests/cases/conformance/types/import/externs")
>a : any
>new Foo({doer: Foo.Bar}) : import("tests/cases/conformance/types/import/externs")
>Foo : typeof import("tests/cases/conformance/types/import/externs")
>{doer: Foo.Bar} : { doer: (x: string, y?: number) => void; }
Expand Down
5 changes: 4 additions & 1 deletion tests/baselines/reference/jsdocTypeTagCast.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,7 @@ tests/cases/conformance/jsdoc/b.js(67,8): error TS2454: Variable 'numOrStr' is u
}



var asConst1 = /** @type {const} */(1);
var asConst2 = /** @type {const} */({
x: 1
});
9 changes: 8 additions & 1 deletion tests/baselines/reference/jsdocTypeTagCast.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ if(/** @type {numOrStr is string} */(numOrStr === undefined)) { // Error
}



var asConst1 = /** @type {const} */(1);
var asConst2 = /** @type {const} */({
x: 1
});

//// [a.js]
var W;
Expand Down Expand Up @@ -154,3 +157,7 @@ var str;
if ( /** @type {numOrStr is string} */(numOrStr === undefined)) { // Error
str = numOrStr; // Error, no narrowing occurred
}
var asConst1 = /** @type {const} */ (1);
var asConst2 = /** @type {const} */ ({
x: 1
});
9 changes: 9 additions & 0 deletions tests/baselines/reference/jsdocTypeTagCast.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,13 @@ if(/** @type {numOrStr is string} */(numOrStr === undefined)) { // Error
}


var asConst1 = /** @type {const} */(1);
>asConst1 : Symbol(asConst1, Decl(b.js, 70, 3))

var asConst2 = /** @type {const} */({
>asConst2 : Symbol(asConst2, Decl(b.js, 71, 3))

x: 1
>x : Symbol(x, Decl(b.js, 71, 37))

});
14 changes: 14 additions & 0 deletions tests/baselines/reference/jsdocTypeTagCast.types
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,18 @@ if(/** @type {numOrStr is string} */(numOrStr === undefined)) { // Error
}


var asConst1 = /** @type {const} */(1);
>asConst1 : 1
>(1) : 1
>1 : 1

var asConst2 = /** @type {const} */({
>asConst2 : { x: number; }
>({ x: 1}) : { x: number; }
>{ x: 1} : { x: number; }

x: 1
>x : number
>1 : 1

});
4 changes: 4 additions & 0 deletions tests/cases/conformance/jsdoc/jsdocTypeTagCast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,7 @@ if(/** @type {numOrStr is string} */(numOrStr === undefined)) { // Error
}


var asConst1 = /** @type {const} */(1);
var asConst2 = /** @type {const} */({
x: 1
});

0 comments on commit 7b70f43

Please sign in to comment.