Skip to content

Commit 6906002

Browse files
author
Andy
authored
Merge pull request #12352 from Microsoft/untyped_module_symbol
Use a symbol for untyped modules to distinguish from unknownSymbol
2 parents 9796557 + de50910 commit 6906002

File tree

7 files changed

+72
-10
lines changed

7 files changed

+72
-10
lines changed

src/compiler/checker.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ namespace ts {
197197
const evolvingArrayTypes: EvolvingArrayType[] = [];
198198

199199
const unknownSymbol = createSymbol(SymbolFlags.Property, "unknown");
200+
const untypedModuleSymbol = createSymbol(SymbolFlags.ValueModule, "<untyped>");
201+
untypedModuleSymbol.exports = createMap<Symbol>();
200202
const resolvingSymbol = createSymbol(0, "__resolving__");
201203

202204
const anyType = createIntrinsicType(TypeFlags.Any, "any");
@@ -1227,7 +1229,7 @@ namespace ts {
12271229

12281230
if (moduleSymbol) {
12291231
let exportDefaultSymbol: Symbol;
1230-
if (isShorthandAmbientModuleSymbol(moduleSymbol)) {
1232+
if (isUntypedOrShorthandAmbientModuleSymbol(moduleSymbol)) {
12311233
exportDefaultSymbol = moduleSymbol;
12321234
}
12331235
else {
@@ -1307,7 +1309,7 @@ namespace ts {
13071309
if (targetSymbol) {
13081310
const name = specifier.propertyName || specifier.name;
13091311
if (name.text) {
1310-
if (isShorthandAmbientModuleSymbol(moduleSymbol)) {
1312+
if (isUntypedOrShorthandAmbientModuleSymbol(moduleSymbol)) {
13111313
return moduleSymbol;
13121314
}
13131315

@@ -1560,15 +1562,19 @@ namespace ts {
15601562
if (isForAugmentation) {
15611563
const diag = Diagnostics.Invalid_module_name_in_augmentation_Module_0_resolves_to_an_untyped_module_at_1_which_cannot_be_augmented;
15621564
error(errorNode, diag, moduleReference, resolvedModule.resolvedFileName);
1565+
return undefined;
15631566
}
15641567
else if (compilerOptions.noImplicitAny && moduleNotFoundError) {
15651568
error(errorNode,
15661569
Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type,
15671570
moduleReference,
15681571
resolvedModule.resolvedFileName);
1572+
return undefined;
15691573
}
1570-
// Failed imports and untyped modules are both treated in an untyped manner; only difference is whether we give a diagnostic first.
1571-
return undefined;
1574+
// Unlike a failed import, an untyped module produces a dummy symbol.
1575+
// This is checked for by `isUntypedOrShorthandAmbientModuleSymbol`.
1576+
// This must be different than `unknownSymbol` because `getBaseConstructorTypeOfClass` won't fail for `unknownSymbol`.
1577+
return untypedModuleSymbol;
15721578
}
15731579

15741580
if (moduleNotFoundError) {
@@ -3753,7 +3759,7 @@ namespace ts {
37533759
function getTypeOfFuncClassEnumModule(symbol: Symbol): Type {
37543760
const links = getSymbolLinks(symbol);
37553761
if (!links.type) {
3756-
if (symbol.flags & SymbolFlags.Module && isShorthandAmbientModuleSymbol(symbol)) {
3762+
if (symbol.flags & SymbolFlags.Module && isUntypedOrShorthandAmbientModuleSymbol(symbol)) {
37573763
links.type = anyType;
37583764
}
37593765
else {
@@ -21316,7 +21322,7 @@ namespace ts {
2131621322

2131721323
function moduleExportsSomeValue(moduleReferenceExpression: Expression): boolean {
2131821324
let moduleSymbol = resolveExternalModuleName(moduleReferenceExpression.parent, moduleReferenceExpression);
21319-
if (!moduleSymbol || isShorthandAmbientModuleSymbol(moduleSymbol)) {
21325+
if (!moduleSymbol || isUntypedOrShorthandAmbientModuleSymbol(moduleSymbol)) {
2132021326
// If the module is not found or is shorthand, assume that it may export a value.
2132121327
return true;
2132221328
}

src/compiler/utilities.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,8 +435,8 @@ namespace ts {
435435
}
436436

437437
/** Given a symbol for a module, checks that it is either an untyped import or a shorthand ambient module. */
438-
export function isShorthandAmbientModuleSymbol(moduleSymbol: Symbol): boolean {
439-
return isShorthandAmbientModule(moduleSymbol.valueDeclaration);
438+
export function isUntypedOrShorthandAmbientModuleSymbol(moduleSymbol: Symbol): boolean {
439+
return !moduleSymbol.declarations || isShorthandAmbientModule(moduleSymbol.valueDeclaration);
440440
}
441441

442442
function isShorthandAmbientModule(node: Node): boolean {

src/services/findAllReferences.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ namespace ts.FindAllReferences {
133133
return { symbol };
134134
}
135135

136-
if (ts.isShorthandAmbientModuleSymbol(aliasedSymbol)) {
136+
if (ts.isUntypedOrShorthandAmbientModuleSymbol(aliasedSymbol)) {
137137
return { symbol, shorthandModuleSymbol: aliasedSymbol };
138138
}
139139

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/a.ts(2,17): error TS2507: Type 'any' is not a constructor function type.
2+
3+
4+
==== /a.ts (1 errors) ====
5+
import Foo from "foo";
6+
class A extends Foo { }
7+
~~~
8+
!!! error TS2507: Type 'any' is not a constructor function type.
9+
10+
==== /node_modules/foo/index.js (0 errors) ====
11+
// Test that extending an untyped module is an error, unlike extending unknownSymbol.
12+
13+
This file is not read.
14+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//// [tests/cases/compiler/extendsUntypedModule.ts] ////
2+
3+
//// [index.js]
4+
// Test that extending an untyped module is an error, unlike extending unknownSymbol.
5+
6+
This file is not read.
7+
8+
//// [a.ts]
9+
import Foo from "foo";
10+
class A extends Foo { }
11+
12+
13+
//// [a.js]
14+
"use strict";
15+
var __extends = (this && this.__extends) || (function () {
16+
var extendStatics = Object.setPrototypeOf ||
17+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
18+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
19+
return function (d, b) {
20+
extendStatics(d, b);
21+
function __() { this.constructor = d; }
22+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
23+
};
24+
})();
25+
exports.__esModule = true;
26+
var foo_1 = require("foo");
27+
var A = (function (_super) {
28+
__extends(A, _super);
29+
function A() {
30+
return _super !== null && _super.apply(this, arguments) || this;
31+
}
32+
return A;
33+
}(foo_1["default"]));
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Test that extending an untyped module is an error, unlike extending unknownSymbol.
2+
// @noImplicitReferences: true
3+
4+
// @Filename: /node_modules/foo/index.js
5+
This file is not read.
6+
7+
// @Filename: /a.ts
8+
import Foo from "foo";
9+
class A extends Foo { }

tests/cases/fourslash/untypedModuleImport.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ verify.numberOfErrorsInCurrentFile(0);
1212

1313
goTo.marker("fooModule");
1414
verify.goToDefinitionIs([]);
15-
verify.quickInfoIs("");
15+
verify.quickInfoIs("module <untyped>");
1616
verify.noReferences();
1717

1818
goTo.marker("foo");

0 commit comments

Comments
 (0)