Skip to content

fix(45233): export default can not be typed via jsdoc #45342

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9093,7 +9093,7 @@ namespace ts {
}
let type: Type;
if (declaration.kind === SyntaxKind.ExportAssignment) {
type = widenTypeForVariableLikeDeclaration(checkExpressionCached((declaration as ExportAssignment).expression), declaration);
type = widenTypeForVariableLikeDeclaration(tryGetTypeFromEffectiveTypeNode(declaration) || checkExpressionCached((declaration as ExportAssignment).expression), declaration);
}
else if (
isBinaryExpression(declaration) ||
Expand Down Expand Up @@ -9349,13 +9349,15 @@ namespace ts {
if (!links.type) {
const targetSymbol = resolveAlias(symbol);
const exportSymbol = symbol.declarations && getTargetOfAliasDeclaration(getDeclarationOfAliasSymbol(symbol)!, /*dontResolveAlias*/ true);
const declaredType = firstDefined(exportSymbol?.declarations, d => isExportAssignment(d) ? tryGetTypeFromEffectiveTypeNode(d) : undefined);
// It only makes sense to get the type of a value symbol. If the result of resolving
// the alias is not a value, then it has no type. To get the type associated with a
// type symbol, call getDeclaredTypeOfSymbol.
// This check is important because without it, a call to getTypeOfSymbol could end
// up recursively calling getTypeOfAlias, causing a stack overflow.
links.type = exportSymbol?.declarations && isDuplicatedCommonJSExport(exportSymbol.declarations) && symbol.declarations!.length ? getFlowTypeFromCommonJSExport(exportSymbol)
: isDuplicatedCommonJSExport(symbol.declarations) ? autoType
: declaredType ? declaredType
: targetSymbol.flags & SymbolFlags.Value ? getTypeOfSymbol(targetSymbol)
: errorType;
}
Expand Down Expand Up @@ -38841,11 +38843,16 @@ namespace ts {
if (!checkGrammarDecoratorsAndModifiers(node) && hasEffectiveModifiers(node)) {
grammarErrorOnFirstToken(node, Diagnostics.An_export_assignment_cannot_have_modifiers);
}

const typeAnnotationNode = getEffectiveTypeAnnotationNode(node);
if (typeAnnotationNode) {
checkTypeAssignableTo(checkExpressionCached(node.expression), getTypeFromTypeNode(typeAnnotationNode), node.expression);
}

if (node.expression.kind === SyntaxKind.Identifier) {
const id = node.expression as Identifier;
const sym = resolveEntityName(id, SymbolFlags.All, /*ignoreErrors*/ true, /*dontResolveAlias*/ true, node);
if (sym) {

markAliasReferenced(sym, id);
// If not a value, we're interpreting the identifier as a type export, along the lines of (`export { Id as default }`)
const target = sym.flags & SymbolFlags.Alias ? resolveAlias(sym) : sym;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
tests/cases/compiler/a.js(8,18): error TS2322: Type '{ c: boolean; }' is not assignable to type 'Foo'.
Object literal may only specify known properties, and 'c' does not exist in type 'Foo'.


==== tests/cases/compiler/checkJsdocTypeTagOnExportAssignment1.js (0 errors) ====

==== tests/cases/compiler/a.js (1 errors) ====
/**
* @typedef {Object} Foo
* @property {boolean} a
* @property {boolean} b
*/

/** @type {Foo} */
export default { c: false };
~~~~~~~~
!!! error TS2322: Type '{ c: boolean; }' is not assignable to type 'Foo'.
!!! error TS2322: Object literal may only specify known properties, and 'c' does not exist in type 'Foo'.

==== tests/cases/compiler/b.js (0 errors) ====
import a from "./a";
a;

35 changes: 35 additions & 0 deletions tests/baselines/reference/checkJsdocTypeTagOnExportAssignment1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//// [tests/cases/compiler/checkJsdocTypeTagOnExportAssignment1.ts] ////

//// [checkJsdocTypeTagOnExportAssignment1.js]

//// [a.js]
/**
* @typedef {Object} Foo
* @property {boolean} a
* @property {boolean} b
*/

/** @type {Foo} */
export default { c: false };

//// [b.js]
import a from "./a";
a;


//// [checkJsdocTypeTagOnExportAssignment1.js]
//// [a.js]
"use strict";
/**
* @typedef {Object} Foo
* @property {boolean} a
* @property {boolean} b
*/
exports.__esModule = true;
/** @type {Foo} */
exports["default"] = { c: false };
//// [b.js]
"use strict";
exports.__esModule = true;
var a_1 = require("./a");
a_1["default"];
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
=== tests/cases/compiler/checkJsdocTypeTagOnExportAssignment1.js ===

No type information for this code.=== tests/cases/compiler/a.js ===
/**
* @typedef {Object} Foo
* @property {boolean} a
* @property {boolean} b
*/

/** @type {Foo} */
export default { c: false };
>c : Symbol(c, Decl(a.js, 7, 16))

=== tests/cases/compiler/b.js ===
import a from "./a";
>a : Symbol(a, Decl(b.js, 0, 6))

a;
>a : Symbol(a, Decl(b.js, 0, 6))

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
=== tests/cases/compiler/checkJsdocTypeTagOnExportAssignment1.js ===

No type information for this code.=== tests/cases/compiler/a.js ===
/**
* @typedef {Object} Foo
* @property {boolean} a
* @property {boolean} b
*/

/** @type {Foo} */
export default { c: false };
>{ c: false } : { c: boolean; }
>c : boolean
>false : false

=== tests/cases/compiler/b.js ===
import a from "./a";
>a : import("tests/cases/compiler/a").Foo

a;
>a : import("tests/cases/compiler/a").Foo

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
tests/cases/compiler/b.js(2,18): error TS2322: Type '{ c: boolean; }' is not assignable to type 'Foo'.
Object literal may only specify known properties, and 'c' does not exist in type 'Foo'.


==== tests/cases/compiler/checkJsdocTypeTagOnExportAssignment2.js (0 errors) ====

==== tests/cases/compiler/a.ts (0 errors) ====
export interface Foo {
a: number;
b: number;
}

==== tests/cases/compiler/b.js (1 errors) ====
/** @type {import("./a").Foo} */
export default { c: false };
~~~~~~~~
!!! error TS2322: Type '{ c: boolean; }' is not assignable to type 'Foo'.
!!! error TS2322: Object literal may only specify known properties, and 'c' does not exist in type 'Foo'.

==== tests/cases/compiler/c.js (0 errors) ====
import b from "./b";
b;

33 changes: 33 additions & 0 deletions tests/baselines/reference/checkJsdocTypeTagOnExportAssignment2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//// [tests/cases/compiler/checkJsdocTypeTagOnExportAssignment2.ts] ////

//// [checkJsdocTypeTagOnExportAssignment2.js]

//// [a.ts]
export interface Foo {
a: number;
b: number;
}

//// [b.js]
/** @type {import("./a").Foo} */
export default { c: false };

//// [c.js]
import b from "./b";
b;


//// [checkJsdocTypeTagOnExportAssignment2.js]
//// [a.js]
"use strict";
exports.__esModule = true;
//// [b.js]
"use strict";
exports.__esModule = true;
/** @type {import("./a").Foo} */
exports["default"] = { c: false };
//// [c.js]
"use strict";
exports.__esModule = true;
var b_1 = require("./b");
b_1["default"];
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
=== tests/cases/compiler/checkJsdocTypeTagOnExportAssignment2.js ===

No type information for this code.=== tests/cases/compiler/a.ts ===
export interface Foo {
>Foo : Symbol(Foo, Decl(a.ts, 0, 0))

a: number;
>a : Symbol(Foo.a, Decl(a.ts, 0, 22))

b: number;
>b : Symbol(Foo.b, Decl(a.ts, 1, 14))
}

=== tests/cases/compiler/b.js ===
/** @type {import("./a").Foo} */
export default { c: false };
>c : Symbol(c, Decl(b.js, 1, 16))

=== tests/cases/compiler/c.js ===
import b from "./b";
>b : Symbol(b, Decl(c.js, 0, 6))

b;
>b : Symbol(b, Decl(c.js, 0, 6))

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
=== tests/cases/compiler/checkJsdocTypeTagOnExportAssignment2.js ===

No type information for this code.=== tests/cases/compiler/a.ts ===
export interface Foo {
a: number;
>a : number

b: number;
>b : number
}

=== tests/cases/compiler/b.js ===
/** @type {import("./a").Foo} */
export default { c: false };
>{ c: false } : { c: boolean; }
>c : boolean
>false : false

=== tests/cases/compiler/c.js ===
import b from "./b";
>b : import("tests/cases/compiler/a").Foo

b;
>b : import("tests/cases/compiler/a").Foo

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
tests/cases/compiler/a.js(10,16): error TS2739: Type '{ c: number; }' is missing the following properties from type 'Foo': a, b


==== tests/cases/compiler/checkJsdocTypeTagOnExportAssignment3.js (0 errors) ====

==== tests/cases/compiler/a.js (1 errors) ====
/**
* @typedef {Object} Foo
* @property {boolean} a
* @property {boolean} b
*/

const bar = { c: 1 };

/** @type {Foo} */
export default bar;
~~~
!!! error TS2739: Type '{ c: number; }' is missing the following properties from type 'Foo': a, b

==== tests/cases/compiler/b.js (0 errors) ====
import a from "./a";
a;

38 changes: 38 additions & 0 deletions tests/baselines/reference/checkJsdocTypeTagOnExportAssignment3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//// [tests/cases/compiler/checkJsdocTypeTagOnExportAssignment3.ts] ////

//// [checkJsdocTypeTagOnExportAssignment3.js]

//// [a.js]
/**
* @typedef {Object} Foo
* @property {boolean} a
* @property {boolean} b
*/

const bar = { c: 1 };

/** @type {Foo} */
export default bar;

//// [b.js]
import a from "./a";
a;


//// [checkJsdocTypeTagOnExportAssignment3.js]
//// [a.js]
"use strict";
/**
* @typedef {Object} Foo
* @property {boolean} a
* @property {boolean} b
*/
exports.__esModule = true;
var bar = { c: 1 };
/** @type {Foo} */
exports["default"] = bar;
//// [b.js]
"use strict";
exports.__esModule = true;
var a_1 = require("./a");
a_1["default"];
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
=== tests/cases/compiler/checkJsdocTypeTagOnExportAssignment3.js ===

No type information for this code.=== tests/cases/compiler/a.js ===
/**
* @typedef {Object} Foo
* @property {boolean} a
* @property {boolean} b
*/

const bar = { c: 1 };
>bar : Symbol(bar, Decl(a.js, 6, 5))
>c : Symbol(c, Decl(a.js, 6, 13))

/** @type {Foo} */
export default bar;
>bar : Symbol(bar, Decl(a.js, 6, 5))

=== tests/cases/compiler/b.js ===
import a from "./a";
>a : Symbol(a, Decl(b.js, 0, 6))

a;
>a : Symbol(a, Decl(b.js, 0, 6))

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
=== tests/cases/compiler/checkJsdocTypeTagOnExportAssignment3.js ===

No type information for this code.=== tests/cases/compiler/a.js ===
/**
* @typedef {Object} Foo
* @property {boolean} a
* @property {boolean} b
*/

const bar = { c: 1 };
>bar : { c: number; }
>{ c: 1 } : { c: number; }
>c : number
>1 : 1

/** @type {Foo} */
export default bar;
>bar : { c: number; }

=== tests/cases/compiler/b.js ===
import a from "./a";
>a : import("tests/cases/compiler/a").Foo

a;
>a : import("tests/cases/compiler/a").Foo

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
tests/cases/compiler/a.js(6,16): error TS2322: Type 'string' is not assignable to type 'number'.


==== tests/cases/compiler/checkJsdocTypeTagOnExportAssignment4.js (0 errors) ====

==== tests/cases/compiler/a.js (1 errors) ====
/**
* @typedef {number} Foo
*/

/** @type {Foo} */
export default "";
~~
!!! error TS2322: Type 'string' is not assignable to type 'number'.


Loading