Skip to content

Commit 5564ab5

Browse files
committed
Infer typedef variable declaration to never
Typedefs have two forms: specify a name in the `@typedef` declaration or use the name of the variable declaration it is attached to. Since `@typedef` types should only live in the type space, any reference to a variable that has a `@typedef` attached to is illegal. As such, assign it the `never` type if the `@typedef` itself does not specify a name. Fixes microsoft#36375
1 parent 3054500 commit 5564ab5

10 files changed

+47
-3
lines changed

src/compiler/checker.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7084,6 +7084,14 @@ namespace ts {
70847084
return addOptionality(declaredType, isOptional);
70857085
}
70867086

7087+
const jsDocTypeDefTag = getJSDocTypedefTag(declaration);
7088+
// The typedef is attached to a variable declaration, instead of specifying the name in the typedef itself
7089+
// This means that the variable declared references the typedef and should not be used in regular control
7090+
// flow of the program, only in jsdoc type references.
7091+
if (jsDocTypeDefTag && !jsDocTypeDefTag.name && isVariableDeclaration(declaration)) {
7092+
return neverType;
7093+
}
7094+
70877095
if ((noImplicitAny || isInJSFile(declaration)) &&
70887096
declaration.kind === SyntaxKind.VariableDeclaration && !isBindingPattern(declaration.name) &&
70897097
!(getCombinedModifierFlags(declaration) & ModifierFlags.Export) && !(declaration.flags & NodeFlags.Ambient)) {

src/compiler/utilitiesPublic.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,11 @@ namespace ts {
716716
return getFirstJSDocTag(node, isJSDocTemplateTag);
717717
}
718718

719+
/** Gets the JSDoc typedef tag for the node if present */
720+
export function getJSDocTypedefTag(node: Node): JSDocTypedefTag | undefined {
721+
return getFirstJSDocTag(node, isJSDocTypedefTag);
722+
}
723+
719724
/** Gets the JSDoc type tag for the node if present and valid */
720725
export function getJSDocTypeTag(node: Node): JSDocTypeTag | undefined {
721726
// We should have already issued an error if there were multiple type jsdocs, so just use the first one.

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3524,6 +3524,8 @@ declare namespace ts {
35243524
function getJSDocReturnTag(node: Node): JSDocReturnTag | undefined;
35253525
/** Gets the JSDoc template tag for the node if present */
35263526
function getJSDocTemplateTag(node: Node): JSDocTemplateTag | undefined;
3527+
/** Gets the JSDoc typedef tag for the node if present */
3528+
function getJSDocTypedefTag(node: Node): JSDocTypedefTag | undefined;
35273529
/** Gets the JSDoc type tag for the node if present and valid */
35283530
function getJSDocTypeTag(node: Node): JSDocTypeTag | undefined;
35293531
/**

tests/baselines/reference/api/typescript.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3524,6 +3524,8 @@ declare namespace ts {
35243524
function getJSDocReturnTag(node: Node): JSDocReturnTag | undefined;
35253525
/** Gets the JSDoc template tag for the node if present */
35263526
function getJSDocTemplateTag(node: Node): JSDocTemplateTag | undefined;
3527+
/** Gets the JSDoc typedef tag for the node if present */
3528+
function getJSDocTypedefTag(node: Node): JSDocTypedefTag | undefined;
35273529
/** Gets the JSDoc type tag for the node if present and valid */
35283530
function getJSDocTypeTag(node: Node): JSDocTypeTag | undefined;
35293531
/**
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
=== tests/cases/conformance/jsdoc/index.js ===
2+
/** @typedef {{foo: number}} */
3+
export let a;
4+
>a : Symbol(a, Decl(index.js, 1, 10), Decl(index.js, 0, 4))
5+
6+
/** @typedef {number} */
7+
let b;
8+
>b : Symbol(b, Decl(index.js, 3, 3), Decl(index.js, 2, 4))
9+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
=== tests/cases/conformance/jsdoc/index.js ===
2+
/** @typedef {{foo: number}} */
3+
export let a;
4+
>a : never
5+
6+
/** @typedef {number} */
7+
let b;
8+
>b : never
9+

tests/baselines/reference/jsdocTypedefNoCrash.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
* }}
55
*/
66
export const foo = 5;
7-
>foo : 5
7+
>foo : never
88
>5 : 5
99

tests/baselines/reference/jsdocTypedefNoCrash2.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ export type foo = 5;
77
* }}
88
*/
99
export const foo = 5;
10-
>foo : 5
10+
>foo : never
1111
>5 : 5
1212

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// @allowJs: true
2+
// @checkJs: true
3+
// @noEmit: true
4+
// @noImplicitAny: true
5+
// @Filename: index.js
6+
/** @typedef {{foo: number}} */
7+
export let a;
8+
/** @typedef {number} */
9+
let b;

tests/cases/fourslash/jsdocTypedefTagSemanticMeaning1.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99
/////** @type {[|T|]} */
1010
////const n = [|T|];
1111

12-
verify.singleReferenceGroup("type T = number\nconst T: 1", "T");
12+
verify.singleReferenceGroup("type T = number\nconst T: never", "T");

0 commit comments

Comments
 (0)