Skip to content

Commit 122babd

Browse files
committed
check @type tag on require
1 parent f213707 commit 122babd

5 files changed

+44
-21
lines changed

src/compiler/checker.ts

+29-6
Original file line numberDiff line numberDiff line change
@@ -2390,13 +2390,30 @@ namespace ts {
23902390
const immediate = resolveExternalModuleName(node, isVariableDeclaration(node)
23912391
? (getFirstPropertyAccessExpression(node.initializer!) as CallExpression).arguments[0]
23922392
: getExternalModuleImportEqualsDeclarationExpression(node));
2393-
const resolved = resolveExternalModuleSymbol(immediate);
2394-
if (isVariableDeclaration(node) && node.initializer && isPropertyAccessExpression(node.initializer)) {
2395-
// TODO: Relies on old code in resolveCallExpression that special-cases `require("x")`
2396-
return isIdentifier(node.initializer.name)
2397-
? getPropertyOfType(checkExpression(node.initializer.expression), node.initializer.name.escapedText)
2398-
: undefined;
2393+
if (isVariableDeclaration(node)) {
2394+
const typeTag = getJSDocTypeTag(node);
2395+
if (node.initializer && isPropertyAccessExpression(node.initializer)) {
2396+
if (!isIdentifier(node.initializer.name))
2397+
return undefined;
2398+
// TODO: Relies on old code in resolveCallExpression that special-cases `require("x")`
2399+
const original = getPropertyOfType(checkExpression(node.initializer.expression), node.initializer.name.escapedText);
2400+
if (typeTag && original) {
2401+
const symbol: TransientSymbol = cloneSymbol(original) as TransientSymbol
2402+
symbol.type = getTypeFromTypeNode(typeTag.typeExpression);
2403+
return symbol;
2404+
}
2405+
// TODO: Might still want to resolveExternalModuleSymbol here?
2406+
return original;
2407+
}
2408+
else if (typeTag && immediate) {
2409+
// TODO: This is basically wrong.
2410+
const symbol: TransientSymbol = cloneSymbol(immediate) as TransientSymbol
2411+
symbol.type = getTypeFromTypeNode(typeTag.typeExpression);
2412+
return symbol;
2413+
}
2414+
return resolveExternalModuleSymbol(immediate);
23992415
}
2416+
const resolved = resolveExternalModuleSymbol(immediate);
24002417
markSymbolOfAliasDeclarationIfTypeOnly(node, immediate, resolved, /*overwriteEmpty*/ false);
24012418
return resolved;
24022419
}
@@ -32773,6 +32790,12 @@ namespace ts {
3277332790
const symbol = getSymbolOfNode(node);
3277432791
if (/*isBindingElement(node) && */isRequireVariableDeclaration(node, /*requireStringLiteralLikeArgument*/ true) && symbol.flags & SymbolFlags.Alias) {
3277532792
checkAliasSymbol(node);
32793+
const typeTag = getJSDocTypeTag(node);
32794+
const initializer = node.initializer;
32795+
if (typeTag) {
32796+
const type = getTypeFromTypeNode(typeTag.typeExpression);
32797+
checkTypeAssignableToAndOptionallyElaborate(checkExpressionCached(initializer), type, node, initializer, /*headMessage*/ undefined);
32798+
}
3277632799
return;
3277732800
}
3277832801

tests/baselines/reference/checkExportsObjectAssignProperty.symbols

+4-4
Original file line numberDiff line numberDiff line change
@@ -269,18 +269,18 @@ Object.defineProperty(module.exports, "setonlyAccessor", {
269269
*/
270270
const q = require("./mod1").thing;
271271
>q : Symbol(q, Decl(index.js, 3, 5))
272-
>require("./mod1").thing : Symbol(thing, Decl(mod1.js, 0, 0))
272+
>require("./mod1").thing : Symbol(q, Decl(mod1.js, 0, 0))
273273
>require : Symbol(require)
274274
>"./mod1" : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0))
275-
>thing : Symbol(thing, Decl(mod1.js, 0, 0))
275+
>thing : Symbol(q, Decl(mod1.js, 0, 0))
276276

277277
/**
278278
* @type {string}
279279
*/
280280
const u = require("./mod2").thing;
281281
>u : Symbol(u, Decl(index.js, 8, 5))
282-
>require("./mod2").thing : Symbol(thing, Decl(mod2.js, 0, 0))
282+
>require("./mod2").thing : Symbol(u, Decl(mod2.js, 0, 0))
283283
>require : Symbol(require)
284284
>"./mod2" : Symbol("tests/cases/conformance/jsdoc/mod2", Decl(mod2.js, 0, 0))
285-
>thing : Symbol(thing, Decl(mod2.js, 0, 0))
285+
>thing : Symbol(u, Decl(mod2.js, 0, 0))
286286

tests/baselines/reference/requireOfJsonFileInJsFile.errors.txt

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
/user.js(2,7): error TS2339: Property 'b' does not exist on type '{ a: number; }'.
1+
/user.js(2,7): error TS2339: Property 'b' does not exist on type 'typeof "/json"'.
22
/user.js(5,7): error TS2741: Property 'b' is missing in type '{ a: number; }' but required in type '{ b: number; }'.
3-
/user.js(9,7): error TS2339: Property 'b' does not exist on type '{ a: number; }'.
3+
/user.js(9,7): error TS2339: Property 'b' does not exist on type 'typeof "/json"'.
44
/user.js(12,7): error TS2741: Property 'b' is missing in type '{ a: number; }' but required in type '{ b: number; }'.
55

66

77
==== /user.js (4 errors) ====
88
const json0 = require("./json.json");
99
json0.b; // Error (good)
1010
~
11-
!!! error TS2339: Property 'b' does not exist on type '{ a: number; }'.
11+
!!! error TS2339: Property 'b' does not exist on type 'typeof "/json"'.
1212

1313
/** @type {{ b: number }} */
1414
const json1 = require("./json.json"); // No error (bad)
@@ -20,7 +20,7 @@
2020
const js0 = require("./js.js");
2121
json0.b; // Error (good)
2222
~
23-
!!! error TS2339: Property 'b' does not exist on type '{ a: number; }'.
23+
!!! error TS2339: Property 'b' does not exist on type 'typeof "/json"'.
2424

2525
/** @type {{ b: number }} */
2626
const js1 = require("./js.js"); // Error (good)

tests/baselines/reference/requireOfJsonFileInJsFile.symbols

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
const json0 = require("./json.json");
33
>json0 : Symbol(json0, Decl(user.js, 0, 5))
44
>require : Symbol(require)
5-
>"./json.json" : Symbol("/json", Decl(json.json, 0, 0))
5+
>"./json.json" : Symbol(json0, Decl(json.json, 0, 0))
66

77
json0.b; // Error (good)
88
>json0 : Symbol(json0, Decl(user.js, 0, 5))
@@ -11,7 +11,7 @@ json0.b; // Error (good)
1111
const json1 = require("./json.json"); // No error (bad)
1212
>json1 : Symbol(json1, Decl(user.js, 4, 5))
1313
>require : Symbol(require)
14-
>"./json.json" : Symbol("/json", Decl(json.json, 0, 0))
14+
>"./json.json" : Symbol(json0, Decl(json.json, 0, 0))
1515

1616
json1.b; // No error (OK since that's the type annotation)
1717
>json1.b : Symbol(b, Decl(user.js, 3, 12))
@@ -21,7 +21,7 @@ json1.b; // No error (OK since that's the type annotation)
2121
const js0 = require("./js.js");
2222
>js0 : Symbol(js0, Decl(user.js, 7, 5))
2323
>require : Symbol(require)
24-
>"./js.js" : Symbol("/js", Decl(js.js, 0, 0))
24+
>"./js.js" : Symbol(js0, Decl(js.js, 0, 0))
2525

2626
json0.b; // Error (good)
2727
>json0 : Symbol(json0, Decl(user.js, 0, 5))
@@ -30,7 +30,7 @@ json0.b; // Error (good)
3030
const js1 = require("./js.js"); // Error (good)
3131
>js1 : Symbol(js1, Decl(user.js, 11, 5))
3232
>require : Symbol(require)
33-
>"./js.js" : Symbol("/js", Decl(js.js, 0, 0))
33+
>"./js.js" : Symbol(js0, Decl(js.js, 0, 0))
3434

3535
js1.b;
3636
>js1.b : Symbol(b, Decl(user.js, 10, 12))

tests/baselines/reference/requireOfJsonFileInJsFile.types

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
=== /user.js ===
22
const json0 = require("./json.json");
3-
>json0 : { a: number; }
3+
>json0 : typeof json0
44
>require("./json.json") : { a: number; }
55
>require : any
66
>"./json.json" : "./json.json"
77

88
json0.b; // Error (good)
99
>json0.b : any
10-
>json0 : { a: number; }
10+
>json0 : typeof json0
1111
>b : any
1212

1313
/** @type {{ b: number }} */
@@ -30,7 +30,7 @@ const js0 = require("./js.js");
3030

3131
json0.b; // Error (good)
3232
>json0.b : any
33-
>json0 : { a: number; }
33+
>json0 : typeof json0
3434
>b : any
3535

3636
/** @type {{ b: number }} */

0 commit comments

Comments
 (0)