Skip to content

Commit 71b7d44

Browse files
author
Andy
authored
Merge pull request microsoft#8945 from Microsoft/shorthand_ambient_module
Support shorthand ambient module declarations
2 parents e6eb36e + 077dfff commit 71b7d44

35 files changed

+436
-28
lines changed

src/compiler/binder.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ namespace ts {
5353
return state;
5454
}
5555
else if (node.kind === SyntaxKind.ModuleDeclaration) {
56-
return getModuleInstanceState((<ModuleDeclaration>node).body);
56+
const body = (<ModuleDeclaration>node).body;
57+
return body ? getModuleInstanceState(body) : ModuleInstanceState.Instantiated;
5758
}
5859
else {
5960
return ModuleInstanceState.Instantiated;
@@ -1258,7 +1259,7 @@ namespace ts {
12581259

12591260
function hasExportDeclarations(node: ModuleDeclaration | SourceFile): boolean {
12601261
const body = node.kind === SyntaxKind.SourceFile ? node : (<ModuleDeclaration>node).body;
1261-
if (body.kind === SyntaxKind.SourceFile || body.kind === SyntaxKind.ModuleBlock) {
1262+
if (body && (body.kind === SyntaxKind.SourceFile || body.kind === SyntaxKind.ModuleBlock)) {
12621263
for (const stat of (<Block>body).statements) {
12631264
if (stat.kind === SyntaxKind.ExportDeclaration || stat.kind === SyntaxKind.ExportAssignment) {
12641265
return true;

src/compiler/checker.ts

+25-6
Original file line numberDiff line numberDiff line change
@@ -992,7 +992,9 @@ namespace ts {
992992
const moduleSymbol = resolveExternalModuleName(node, (<ImportDeclaration>node.parent).moduleSpecifier);
993993

994994
if (moduleSymbol) {
995-
const exportDefaultSymbol = moduleSymbol.exports["export="] ?
995+
const exportDefaultSymbol = isShorthandAmbientModule(moduleSymbol.valueDeclaration) ?
996+
moduleSymbol :
997+
moduleSymbol.exports["export="] ?
996998
getPropertyOfType(getTypeOfSymbol(moduleSymbol.exports["export="]), "default") :
997999
resolveSymbol(moduleSymbol.exports["default"]);
9981000

@@ -1066,6 +1068,10 @@ namespace ts {
10661068
if (targetSymbol) {
10671069
const name = specifier.propertyName || specifier.name;
10681070
if (name.text) {
1071+
if (isShorthandAmbientModule(moduleSymbol.valueDeclaration)) {
1072+
return moduleSymbol;
1073+
}
1074+
10691075
let symbolFromVariable: Symbol;
10701076
// First check if module was specified with "export=". If so, get the member from the resolved type
10711077
if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports["export="]) {
@@ -3234,9 +3240,14 @@ namespace ts {
32343240
function getTypeOfFuncClassEnumModule(symbol: Symbol): Type {
32353241
const links = getSymbolLinks(symbol);
32363242
if (!links.type) {
3237-
const type = createObjectType(TypeFlags.Anonymous, symbol);
3238-
links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ?
3239-
addTypeKind(type, TypeFlags.Undefined) : type;
3243+
if (symbol.valueDeclaration.kind === SyntaxKind.ModuleDeclaration && isShorthandAmbientModule(<ModuleDeclaration>symbol.valueDeclaration)) {
3244+
links.type = anyType;
3245+
}
3246+
else {
3247+
const type = createObjectType(TypeFlags.Anonymous, symbol);
3248+
links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ?
3249+
addTypeKind(type, TypeFlags.Undefined) : type;
3250+
}
32403251
}
32413252
return links.type;
32423253
}
@@ -16052,7 +16063,7 @@ namespace ts {
1605216063
// - augmentation for a global scope is always applied
1605316064
// - augmentation for some external module is applied if symbol for augmentation is merged (it was combined with target module).
1605416065
const checkBody = isGlobalAugmentation || (getSymbolOfNode(node).flags & SymbolFlags.Merged);
16055-
if (checkBody) {
16066+
if (checkBody && node.body) {
1605616067
// body of ambient external module is always a module block
1605716068
for (const statement of (<ModuleBlock>node.body).statements) {
1605816069
checkModuleAugmentationElement(statement, isGlobalAugmentation);
@@ -16079,7 +16090,15 @@ namespace ts {
1607916090
}
1608016091
}
1608116092
}
16082-
checkSourceElement(node.body);
16093+
16094+
if (compilerOptions.noImplicitAny && !node.body) {
16095+
// Ambient shorthand module is an implicit any
16096+
reportImplicitAnyError(node, anyType);
16097+
}
16098+
16099+
if (node.body) {
16100+
checkSourceElement(node.body);
16101+
}
1608316102
}
1608416103

1608516104
function checkModuleAugmentationElement(node: Node, isGlobalAugmentation: boolean): void {

src/compiler/declarationEmitter.ts

+15-10
Original file line numberDiff line numberDiff line change
@@ -853,21 +853,26 @@ namespace ts {
853853
writeTextOfNode(currentText, node.name);
854854
}
855855
}
856-
while (node.body.kind !== SyntaxKind.ModuleBlock) {
856+
while (node.body && node.body.kind !== SyntaxKind.ModuleBlock) {
857857
node = <ModuleDeclaration>node.body;
858858
write(".");
859859
writeTextOfNode(currentText, node.name);
860860
}
861861
const prevEnclosingDeclaration = enclosingDeclaration;
862-
enclosingDeclaration = node;
863-
write(" {");
864-
writeLine();
865-
increaseIndent();
866-
emitLines((<ModuleBlock>node.body).statements);
867-
decreaseIndent();
868-
write("}");
869-
writeLine();
870-
enclosingDeclaration = prevEnclosingDeclaration;
862+
if (node.body) {
863+
enclosingDeclaration = node;
864+
write(" {");
865+
writeLine();
866+
increaseIndent();
867+
emitLines((<ModuleBlock>node.body).statements);
868+
decreaseIndent();
869+
write("}");
870+
writeLine();
871+
enclosingDeclaration = prevEnclosingDeclaration;
872+
}
873+
else {
874+
write(";");
875+
}
871876
}
872877

873878
function writeTypeAliasDeclaration(node: TypeAliasDeclaration) {

src/compiler/emitter.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -6300,7 +6300,7 @@ const _super = (function (geti, seti) {
63006300
}
63016301

63026302
function getInnerMostModuleDeclarationFromDottedModule(moduleDeclaration: ModuleDeclaration): ModuleDeclaration {
6303-
if (moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) {
6303+
if (moduleDeclaration.body && moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) {
63046304
const recursiveInnerModule = getInnerMostModuleDeclarationFromDottedModule(<ModuleDeclaration>moduleDeclaration.body);
63056305
return recursiveInnerModule || <ModuleDeclaration>moduleDeclaration.body;
63066306
}
@@ -6349,6 +6349,7 @@ const _super = (function (geti, seti) {
63496349
write(getGeneratedNameForNode(node));
63506350
emitEnd(node.name);
63516351
write(") ");
6352+
Debug.assert(node.body !== undefined); // node.body must exist, as this is a non-ambient module
63526353
if (node.body.kind === SyntaxKind.ModuleBlock) {
63536354
const saveConvertedLoopState = convertedLoopState;
63546355
const saveTempFlags = tempFlags;

src/compiler/parser.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -5337,7 +5337,14 @@ namespace ts {
53375337
else {
53385338
node.name = parseLiteralNode(/*internName*/ true);
53395339
}
5340-
node.body = parseModuleBlock();
5340+
5341+
if (token === SyntaxKind.OpenBraceToken) {
5342+
node.body = parseModuleBlock();
5343+
}
5344+
else {
5345+
parseSemicolon();
5346+
}
5347+
53415348
return finishNode(node);
53425349
}
53435350

src/compiler/program.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -1775,9 +1775,12 @@ namespace ts {
17751775
// The StringLiteral must specify a top - level external module name.
17761776
// Relative external module names are not permitted
17771777

1778-
// NOTE: body of ambient module is always a module block
1779-
for (const statement of (<ModuleBlock>(<ModuleDeclaration>node).body).statements) {
1780-
collectModuleReferences(statement, /*inAmbientModule*/ true);
1778+
// NOTE: body of ambient module is always a module block, if it exists
1779+
const body = <ModuleBlock>(<ModuleDeclaration>node).body;
1780+
if (body) {
1781+
for (const statement of body.statements) {
1782+
collectModuleReferences(statement, /*inAmbientModule*/ true);
1783+
}
17811784
}
17821785
}
17831786
}

src/compiler/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1301,7 +1301,7 @@ namespace ts {
13011301
// @kind(SyntaxKind.ModuleDeclaration)
13021302
export interface ModuleDeclaration extends DeclarationStatement {
13031303
name: Identifier | LiteralExpression;
1304-
body: ModuleBlock | ModuleDeclaration;
1304+
body?: ModuleBlock | ModuleDeclaration;
13051305
}
13061306

13071307
// @kind(SyntaxKind.ModuleBlock)

src/compiler/utilities.ts

+5
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,11 @@ namespace ts {
372372
((<ModuleDeclaration>node).name.kind === SyntaxKind.StringLiteral || isGlobalScopeAugmentation(<ModuleDeclaration>node));
373373
}
374374

375+
export function isShorthandAmbientModule(node: Node): boolean {
376+
// The only kind of module that can be missing a body is a shorthand ambient module.
377+
return node.kind === SyntaxKind.ModuleDeclaration && (!(<ModuleDeclaration>node).body);
378+
}
379+
375380
export function isBlockScopedContainerTopLevel(node: Node): boolean {
376381
return node.kind === SyntaxKind.SourceFile ||
377382
node.kind === SyntaxKind.ModuleDeclaration ||

src/services/navigationBar.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,10 @@ namespace ts.NavigationBar {
188188
case SyntaxKind.ModuleDeclaration:
189189
let moduleDeclaration = <ModuleDeclaration>node;
190190
topLevelNodes.push(node);
191-
addTopLevelNodes((<Block>getInnermostModule(moduleDeclaration).body).statements, topLevelNodes);
191+
const inner = getInnermostModule(moduleDeclaration);
192+
if (inner.body) {
193+
addTopLevelNodes((<Block>inner.body).statements, topLevelNodes);
194+
}
192195
break;
193196

194197
case SyntaxKind.FunctionDeclaration:
@@ -453,7 +456,8 @@ namespace ts.NavigationBar {
453456
function createModuleItem(node: ModuleDeclaration): NavigationBarItem {
454457
const moduleName = getModuleName(node);
455458

456-
const childItems = getItemsWorker(getChildNodes((<Block>getInnermostModule(node).body).statements), createChildItem);
459+
const body = <Block>getInnermostModule(node).body;
460+
const childItems = body ? getItemsWorker(getChildNodes(body.statements), createChildItem) : [];
457461

458462
return getNavigationBarItem(moduleName,
459463
ts.ScriptElementKind.moduleElement,
@@ -611,7 +615,7 @@ namespace ts.NavigationBar {
611615
}
612616

613617
function getInnermostModule(node: ModuleDeclaration): ModuleDeclaration {
614-
while (node.body.kind === SyntaxKind.ModuleDeclaration) {
618+
while (node.body && node.body.kind === SyntaxKind.ModuleDeclaration) {
615619
node = <ModuleDeclaration>node.body;
616620
}
617621

src/services/services.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ namespace ts {
414414
}
415415

416416
// If this is left side of dotted module declaration, there is no doc comments associated with this node
417-
if (declaration.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>declaration).body.kind === SyntaxKind.ModuleDeclaration) {
417+
if (declaration.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>declaration).body && (<ModuleDeclaration>declaration).body.kind === SyntaxKind.ModuleDeclaration) {
418418
return;
419419
}
420420

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//// [tests/cases/conformance/ambient/ambientShorthand.ts] ////
2+
3+
//// [declarations.d.ts]
4+
declare module "jquery"
5+
// Semicolon is optional
6+
declare module "fs";
7+
8+
//// [user.ts]
9+
///<reference path="declarations.d.ts"/>
10+
import foo, {bar} from "jquery";
11+
import * as baz from "fs";
12+
import boom = require("jquery");
13+
foo(bar, baz, boom);
14+
15+
16+
//// [user.js]
17+
"use strict";
18+
///<reference path="declarations.d.ts"/>
19+
var jquery_1 = require("jquery");
20+
var baz = require("fs");
21+
var boom = require("jquery");
22+
jquery_1["default"](jquery_1.bar, baz, boom);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
=== tests/cases/conformance/ambient/user.ts ===
2+
///<reference path="declarations.d.ts"/>
3+
import foo, {bar} from "jquery";
4+
>foo : Symbol(foo, Decl(user.ts, 1, 6))
5+
>bar : Symbol(bar, Decl(user.ts, 1, 13))
6+
7+
import * as baz from "fs";
8+
>baz : Symbol(baz, Decl(user.ts, 2, 6))
9+
10+
import boom = require("jquery");
11+
>boom : Symbol(boom, Decl(user.ts, 2, 26))
12+
13+
foo(bar, baz, boom);
14+
>foo : Symbol(foo, Decl(user.ts, 1, 6))
15+
>bar : Symbol(bar, Decl(user.ts, 1, 13))
16+
>baz : Symbol(baz, Decl(user.ts, 2, 6))
17+
>boom : Symbol(boom, Decl(user.ts, 2, 26))
18+
19+
=== tests/cases/conformance/ambient/declarations.d.ts ===
20+
declare module "jquery"
21+
No type information for this code.// Semicolon is optional
22+
No type information for this code.declare module "fs";
23+
No type information for this code.
24+
No type information for this code.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
=== tests/cases/conformance/ambient/user.ts ===
2+
///<reference path="declarations.d.ts"/>
3+
import foo, {bar} from "jquery";
4+
>foo : any
5+
>bar : any
6+
7+
import * as baz from "fs";
8+
>baz : any
9+
10+
import boom = require("jquery");
11+
>boom : any
12+
13+
foo(bar, baz, boom);
14+
>foo(bar, baz, boom) : any
15+
>foo : any
16+
>bar : any
17+
>baz : any
18+
>boom : any
19+
20+
=== tests/cases/conformance/ambient/declarations.d.ts ===
21+
declare module "jquery"
22+
No type information for this code.// Semicolon is optional
23+
No type information for this code.declare module "fs";
24+
No type information for this code.
25+
No type information for this code.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//// [ambientShorthand_declarationEmit.ts]
2+
declare module "foo";
3+
4+
5+
//// [ambientShorthand_declarationEmit.js]
6+
7+
8+
//// [ambientShorthand_declarationEmit.d.ts]
9+
declare module "foo";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
=== tests/cases/conformance/ambient/ambientShorthand_declarationEmit.ts ===
2+
declare module "foo";
3+
No type information for this code.
4+
No type information for this code.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
=== tests/cases/conformance/ambient/ambientShorthand_declarationEmit.ts ===
2+
declare module "foo";
3+
No type information for this code.
4+
No type information for this code.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//// [tests/cases/conformance/ambient/ambientShorthand_duplicate.ts] ////
2+
3+
//// [declarations1.d.ts]
4+
declare module "foo";
5+
6+
//// [declarations2.d.ts]
7+
declare module "foo";
8+
9+
//// [user.ts]
10+
///<reference path="declarations1.d.ts" />
11+
///<reference path="declarations1.d.ts" />
12+
import foo from "foo";
13+
14+
15+
//// [user.js]
16+
"use strict";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
=== tests/cases/conformance/ambient/user.ts ===
2+
///<reference path="declarations1.d.ts" />
3+
///<reference path="declarations1.d.ts" />
4+
import foo from "foo";
5+
>foo : Symbol(foo, Decl(user.ts, 2, 6))
6+
7+
=== tests/cases/conformance/ambient/declarations1.d.ts ===
8+
declare module "foo";
9+
No type information for this code.
10+
No type information for this code.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
=== tests/cases/conformance/ambient/user.ts ===
2+
///<reference path="declarations1.d.ts" />
3+
///<reference path="declarations1.d.ts" />
4+
import foo from "foo";
5+
>foo : any
6+
7+
=== tests/cases/conformance/ambient/declarations1.d.ts ===
8+
declare module "foo";
9+
No type information for this code.
10+
No type information for this code.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
tests/cases/conformance/ambient/ambientShorthand_isImplicitAny.ts(1,16): error TS7005: Variable '"jquery"' implicitly has an 'any' type.
2+
3+
4+
==== tests/cases/conformance/ambient/ambientShorthand_isImplicitAny.ts (1 errors) ====
5+
declare module "jquery";
6+
~~~~~~~~
7+
!!! error TS7005: Variable '"jquery"' implicitly has an 'any' type.
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//// [ambientShorthand_isImplicitAny.ts]
2+
declare module "jquery";
3+
4+
5+
//// [ambientShorthand_isImplicitAny.js]

0 commit comments

Comments
 (0)