Merge pull request #6495 from Microsoft/moveEmitFlagsToBinder

Move emit helper flags to binder.
This commit is contained in:
Ron Buckton
2016-01-21 10:30:59 -08:00
13 changed files with 429 additions and 105 deletions
+80 -7
View File
@@ -117,6 +117,12 @@ namespace ts {
let labelIndexMap: Map<number>;
let implicitLabels: number[];
// state used for emit helpers
let hasClassExtends: boolean;
let hasAsyncFunctions: boolean;
let hasDecorators: boolean;
let hasParameterDecorators: boolean;
// If this file is an external module, then it is automatically in strict-mode according to
// ES6. If it is not an external module, then we'll determine if it is in strict mode or
// not depending on if we see "use strict" in certain places (or if we hit a class/namespace).
@@ -151,6 +157,10 @@ namespace ts {
labelStack = undefined;
labelIndexMap = undefined;
implicitLabels = undefined;
hasClassExtends = false;
hasAsyncFunctions = false;
hasDecorators = false;
hasParameterDecorators = false;
}
return bindSourceFile;
@@ -351,8 +361,8 @@ namespace ts {
// when the emitter comes back to it, it knows not to qualify the name if it was found in a containing scope.
// NOTE: Nested ambient modules always should go to to 'locals' table to prevent their automatic merge
// during global merging in the checker. Why? The only case when ambient module is permitted inside another module is module augmentation
// and this case is specially handled. Module augmentations should only be merged with original module definition
// during global merging in the checker. Why? The only case when ambient module is permitted inside another module is module augmentation
// and this case is specially handled. Module augmentations should only be merged with original module definition
// and should never be merged directly with other augmentation, and the latter case would be possible if automatic merge is allowed.
if (!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) {
const exportKind =
@@ -429,6 +439,9 @@ namespace ts {
// reset all reachability check related flags on node (for incremental scenarios)
flags &= ~NodeFlags.ReachabilityCheckFlags;
// reset all emit helper flags on node (for incremental scenarios)
flags &= ~NodeFlags.EmitHelperFlags;
if (kind === SyntaxKind.InterfaceDeclaration) {
seenThisKeyword = false;
}
@@ -459,6 +472,21 @@ namespace ts {
flags = seenThisKeyword ? flags | NodeFlags.ContainsThis : flags & ~NodeFlags.ContainsThis;
}
if (kind === SyntaxKind.SourceFile) {
if (hasClassExtends) {
flags |= NodeFlags.HasClassExtends;
}
if (hasDecorators) {
flags |= NodeFlags.HasDecorators;
}
if (hasParameterDecorators) {
flags |= NodeFlags.HasParamDecorators;
}
if (hasAsyncFunctions) {
flags |= NodeFlags.HasAsyncFunctions;
}
}
node.flags = flags;
if (saveState) {
@@ -1255,8 +1283,7 @@ namespace ts {
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.Method | ((<MethodDeclaration>node).questionToken ? SymbolFlags.Optional : SymbolFlags.None),
isObjectLiteralMethod(node) ? SymbolFlags.PropertyExcludes : SymbolFlags.MethodExcludes);
case SyntaxKind.FunctionDeclaration:
checkStrictModeFunctionName(<FunctionDeclaration>node);
return declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.Function, SymbolFlags.FunctionExcludes);
return bindFunctionDeclaration(<FunctionDeclaration>node);
case SyntaxKind.Constructor:
return declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.Constructor, /*symbolExcludes:*/ SymbolFlags.None);
case SyntaxKind.GetAccessor:
@@ -1272,9 +1299,7 @@ namespace ts {
return bindObjectLiteralExpression(<ObjectLiteralExpression>node);
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
checkStrictModeFunctionName(<FunctionExpression>node);
const bindingName = (<FunctionExpression>node).name ? (<FunctionExpression>node).name.text : "__function";
return bindAnonymousDeclaration(<FunctionExpression>node, SymbolFlags.Function, bindingName);
return bindFunctionExpression(<FunctionExpression>node);
case SyntaxKind.CallExpression:
if (isInJavaScriptFile(node)) {
@@ -1424,6 +1449,15 @@ namespace ts {
}
function bindClassLikeDeclaration(node: ClassLikeDeclaration) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (getClassExtendsHeritageClauseElement(node) !== undefined) {
hasClassExtends = true;
}
if (nodeIsDecorated(node)) {
hasDecorators = true;
}
}
if (node.kind === SyntaxKind.ClassDeclaration) {
bindBlockScopedDeclaration(node, SymbolFlags.Class, SymbolFlags.ClassExcludes);
}
@@ -1493,6 +1527,13 @@ namespace ts {
}
function bindParameter(node: ParameterDeclaration) {
if (!isDeclarationFile(file) &&
!isInAmbientContext(node) &&
nodeIsDecorated(node)) {
hasDecorators = true;
hasParameterDecorators = true;
}
if (inStrictMode) {
// It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a
// strict mode FunctionLikeDeclaration or FunctionExpression(13.1)
@@ -1514,7 +1555,39 @@ namespace ts {
}
}
function bindFunctionDeclaration(node: FunctionDeclaration) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (isAsyncFunctionLike(node)) {
hasAsyncFunctions = true;
}
}
checkStrictModeFunctionName(<FunctionDeclaration>node);
return declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.Function, SymbolFlags.FunctionExcludes);
}
function bindFunctionExpression(node: FunctionExpression) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (isAsyncFunctionLike(node)) {
hasAsyncFunctions = true;
}
}
checkStrictModeFunctionName(<FunctionExpression>node);
const bindingName = (<FunctionExpression>node).name ? (<FunctionExpression>node).name.text : "__function";
return bindAnonymousDeclaration(<FunctionExpression>node, SymbolFlags.Function, bindingName);
}
function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (isAsyncFunctionLike(node)) {
hasAsyncFunctions = true;
}
if (nodeIsDecorated(node)) {
hasDecorators = true;
}
}
return hasDynamicName(node)
? bindAnonymousDeclaration(node, symbolFlags, "__computed")
: declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes);
+1 -48
View File
@@ -174,11 +174,6 @@ namespace ts {
const unionTypes: Map<UnionType> = {};
const intersectionTypes: Map<IntersectionType> = {};
const stringLiteralTypes: Map<StringLiteralType> = {};
let emitExtends = false;
let emitDecorate = false;
let emitParam = false;
let emitAwaiter = false;
const emitGenerator = false;
const resolutionTargets: TypeSystemEntity[] = [];
const resolutionResults: boolean[] = [];
@@ -458,7 +453,7 @@ namespace ts {
* Get symbols that represent parameter-property-declaration as parameter and as property declaration
* @param parameter a parameterDeclaration node
* @param parameterName a name of the parameter to get the symbols for.
* @return a tuple of two symbols
* @return a tuple of two symbols
*/
function getSymbolsOfParameterPropertyDeclaration(parameter: ParameterDeclaration, parameterName: string): [Symbol, Symbol] {
const constructoDeclaration = parameter.parent;
@@ -10249,11 +10244,6 @@ namespace ts {
return anyFunctionType;
}
const isAsync = isAsyncFunctionLike(node);
if (isAsync) {
emitAwaiter = true;
}
const links = getNodeLinks(node);
const type = getTypeOfSymbol(node.symbol);
const contextSensitive = isContextSensitive(node);
@@ -10302,10 +10292,6 @@ namespace ts {
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
const isAsync = isAsyncFunctionLike(node);
if (isAsync) {
emitAwaiter = true;
}
const returnOrPromisedType = node.type && (isAsync ? checkAsyncFunctionReturnType(node) : getTypeFromTypeNode(node.type));
if (!node.asteriskToken) {
// return is not necessary in the body of generators
@@ -12442,11 +12428,6 @@ namespace ts {
}
}
emitDecorate = true;
if (node.kind === SyntaxKind.Parameter) {
emitParam = true;
}
forEach(node.decorators, checkDecorator);
}
@@ -12464,9 +12445,6 @@ namespace ts {
checkDecorators(node);
checkSignatureDeclaration(node);
const isAsync = isAsyncFunctionLike(node);
if (isAsync) {
emitAwaiter = true;
}
// Do not use hasDynamicName here, because that returns false for well known symbols.
// We want to perform checkComputedPropertyName for all computed properties, including
@@ -13597,7 +13575,6 @@ namespace ts {
const baseTypeNode = getClassExtendsHeritageClauseElement(node);
if (baseTypeNode) {
emitExtends = emitExtends || !isInAmbientContext(node);
const baseTypes = getBaseTypes(type);
if (baseTypes.length && produceDiagnostics) {
const baseType = baseTypes[0];
@@ -14777,10 +14754,6 @@ namespace ts {
// Grammar checking
checkGrammarSourceFile(node);
emitExtends = false;
emitDecorate = false;
emitParam = false;
emitAwaiter = false;
potentialThisCollisions.length = 0;
deferredNodes = [];
@@ -14797,26 +14770,6 @@ namespace ts {
potentialThisCollisions.length = 0;
}
if (emitExtends) {
links.flags |= NodeCheckFlags.EmitExtends;
}
if (emitDecorate) {
links.flags |= NodeCheckFlags.EmitDecorate;
}
if (emitParam) {
links.flags |= NodeCheckFlags.EmitParam;
}
if (emitAwaiter) {
links.flags |= NodeCheckFlags.EmitAwaiter;
}
if (emitGenerator || (emitAwaiter && languageVersion < ScriptTarget.ES6)) {
links.flags |= NodeCheckFlags.EmitGenerator;
}
links.flags |= NodeCheckFlags.TypeChecked;
}
}
+4 -4
View File
@@ -7349,12 +7349,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
if (!compilerOptions.noEmitHelpers) {
// Only Emit __extends function when target ES5.
// For target ES6 and above, we can emit classDeclaration as is.
if ((languageVersion < ScriptTarget.ES6) && (!extendsEmitted && resolver.getNodeCheckFlags(node) & NodeCheckFlags.EmitExtends)) {
if ((languageVersion < ScriptTarget.ES6) && (!extendsEmitted && node.flags & NodeFlags.HasClassExtends)) {
writeLines(extendsHelper);
extendsEmitted = true;
}
if (!decorateEmitted && resolver.getNodeCheckFlags(node) & NodeCheckFlags.EmitDecorate) {
if (!decorateEmitted && node.flags & NodeFlags.HasDecorators) {
writeLines(decorateHelper);
if (compilerOptions.emitDecoratorMetadata) {
writeLines(metadataHelper);
@@ -7362,12 +7362,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
decorateEmitted = true;
}
if (!paramEmitted && resolver.getNodeCheckFlags(node) & NodeCheckFlags.EmitParam) {
if (!paramEmitted && node.flags & NodeFlags.HasParamDecorators) {
writeLines(paramHelper);
paramEmitted = true;
}
if (!awaiterEmitted && resolver.getNodeCheckFlags(node) & NodeCheckFlags.EmitAwaiter) {
if (!awaiterEmitted && node.flags & NodeFlags.HasAsyncFunctions) {
writeLines(awaiterHelper);
awaiterEmitted = true;
}
+7 -6
View File
@@ -391,11 +391,17 @@ namespace ts {
HasImplicitReturn = 1 << 19, // If function implicitly returns on one of codepaths (initialized by binding)
HasExplicitReturn = 1 << 20, // If function has explicit reachable return on one of codepaths (initialized by binding)
GlobalAugmentation = 1 << 21, // Set if module declaration is an augmentation for the global scope
HasClassExtends = 1 << 22, // If the file has a non-ambient class with an extends clause in ES5 or lower (initialized by binding)
HasDecorators = 1 << 23, // If the file has decorators (initialized by binding)
HasParamDecorators = 1 << 24, // If the file has parameter decorators (initialized by binding)
HasAsyncFunctions = 1 << 25, // If the file has async functions (initialized by binding)
Modifier = Export | Ambient | Public | Private | Protected | Static | Abstract | Default | Async,
AccessibilityModifier = Public | Private | Protected,
BlockScoped = Let | Const,
ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn
ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn,
EmitHelperFlags = HasClassExtends | HasDecorators | HasParamDecorators | HasAsyncFunctions,
}
/* @internal */
@@ -2047,11 +2053,6 @@ namespace ts {
TypeChecked = 0x00000001, // Node has been type checked
LexicalThis = 0x00000002, // Lexical 'this' reference
CaptureThis = 0x00000004, // Lexical 'this' used in body
EmitExtends = 0x00000008, // Emit __extends
EmitDecorate = 0x00000010, // Emit __decorate
EmitParam = 0x00000020, // Emit __param helper for decorators
EmitAwaiter = 0x00000040, // Emit __awaiter
EmitGenerator = 0x00000080, // Emit __generator
SuperInstance = 0x00000100, // Instance 'super' reference
SuperStatic = 0x00000200, // Static 'super' reference
ContextChecked = 0x00000400, // Contextual types have been assigned
+14 -40
View File
@@ -802,8 +802,8 @@ namespace ts {
}
/**
* Given an super call\property node returns a closest node where either
* - super call\property is legal in the node and not legal in the parent node the node.
* Given an super call\property node returns a closest node where either
* - super call\property is legal in the node and not legal in the parent node the node.
* i.e. super call is legal in constructor but not legal in the class body.
* - node is arrow function (so caller might need to call getSuperContainer in case it needs to climb higher)
* - super call\property is definitely illegal in the node (but might be legal in some subnode)
@@ -885,54 +885,28 @@ namespace ts {
// property declarations are valid if their parent is a class declaration.
return node.parent.kind === SyntaxKind.ClassDeclaration;
case SyntaxKind.Parameter:
// if the parameter's parent has a body and its grandparent is a class declaration, this is a valid target;
return (<FunctionLikeDeclaration>node.parent).body && node.parent.parent.kind === SyntaxKind.ClassDeclaration;
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.MethodDeclaration:
// if this method has a body and its parent is a class declaration, this is a valid target.
return (<FunctionLikeDeclaration>node).body && node.parent.kind === SyntaxKind.ClassDeclaration;
return (<FunctionLikeDeclaration>node).body !== undefined
&& node.parent.kind === SyntaxKind.ClassDeclaration;
case SyntaxKind.Parameter:
// if the parameter's parent has a body and its grandparent is a class declaration, this is a valid target;
return (<FunctionLikeDeclaration>node.parent).body !== undefined
&& (node.parent.kind === SyntaxKind.Constructor
|| node.parent.kind === SyntaxKind.MethodDeclaration
|| node.parent.kind === SyntaxKind.SetAccessor)
&& node.parent.parent.kind === SyntaxKind.ClassDeclaration;
}
return false;
}
export function nodeIsDecorated(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
if (node.decorators) {
return true;
}
return false;
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.Parameter:
if (node.decorators) {
return true;
}
return false;
case SyntaxKind.GetAccessor:
if ((<FunctionLikeDeclaration>node).body && node.decorators) {
return true;
}
return false;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.SetAccessor:
if ((<FunctionLikeDeclaration>node).body && node.decorators) {
return true;
}
return false;
}
return false;
return node.decorators !== undefined
&& nodeCanBeDecorated(node);
}
export function isPropertyAccessExpression(node: Node): node is PropertyAccessExpression {
@@ -0,0 +1,47 @@
//// [tests/cases/compiler/asyncFunctionsAcrossFiles.ts] ////
//// [a.ts]
import { b } from './b';
export const a = {
f: async () => {
await b.f();
}
};
//// [b.ts]
import { a } from './a';
export const b = {
f: async () => {
await a.f();
}
};
//// [b.js]
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new P(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.call(thisArg, _arguments)).next());
});
};
import { a } from './a';
export const b = {
f: () => __awaiter(this, void 0, Promise, function* () {
yield a.f();
})
};
//// [a.js]
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new P(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.call(thisArg, _arguments)).next());
});
};
import { b } from './b';
export const a = {
f: () => __awaiter(this, void 0, Promise, function* () {
yield b.f();
})
};
@@ -0,0 +1,32 @@
=== tests/cases/compiler/a.ts ===
import { b } from './b';
>b : Symbol(b, Decl(a.ts, 0, 8))
export const a = {
>a : Symbol(a, Decl(a.ts, 1, 12))
f: async () => {
>f : Symbol(f, Decl(a.ts, 1, 18))
await b.f();
>b.f : Symbol(f, Decl(b.ts, 1, 18))
>b : Symbol(b, Decl(a.ts, 0, 8))
>f : Symbol(f, Decl(b.ts, 1, 18))
}
};
=== tests/cases/compiler/b.ts ===
import { a } from './a';
>a : Symbol(a, Decl(b.ts, 0, 8))
export const b = {
>b : Symbol(b, Decl(b.ts, 1, 12))
f: async () => {
>f : Symbol(f, Decl(b.ts, 1, 18))
await a.f();
>a.f : Symbol(f, Decl(a.ts, 1, 18))
>a : Symbol(a, Decl(b.ts, 0, 8))
>f : Symbol(f, Decl(a.ts, 1, 18))
}
};
@@ -0,0 +1,40 @@
=== tests/cases/compiler/a.ts ===
import { b } from './b';
>b : { f: () => Promise<void>; }
export const a = {
>a : { f: () => Promise<void>; }
>{ f: async () => { await b.f(); }} : { f: () => Promise<void>; }
f: async () => {
>f : () => Promise<void>
>async () => { await b.f(); } : () => Promise<void>
await b.f();
>await b.f() : void
>b.f() : Promise<void>
>b.f : () => Promise<void>
>b : { f: () => Promise<void>; }
>f : () => Promise<void>
}
};
=== tests/cases/compiler/b.ts ===
import { a } from './a';
>a : { f: () => Promise<void>; }
export const b = {
>b : { f: () => Promise<void>; }
>{ f: async () => { await a.f(); }} : { f: () => Promise<void>; }
f: async () => {
>f : () => Promise<void>
>async () => { await a.f(); } : () => Promise<void>
await a.f();
>await a.f() : void
>a.f() : Promise<void>
>a.f : () => Promise<void>
>a : { f: () => Promise<void>; }
>f : () => Promise<void>
}
};
@@ -0,0 +1,71 @@
//// [tests/cases/compiler/classExtendsAcrossFiles.ts] ////
//// [a.ts]
import { b } from './b';
export const a = {
f: () => {
class A { }
class B extends A { }
b.f();
}
};
//// [b.ts]
import { a } from './a';
export const b = {
f: () => {
class A { }
class B extends A { }
a.f();
}
};
//// [b.js]
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var a_1 = require('./a');
exports.b = {
f: function () {
var A = (function () {
function A() {
}
return A;
}());
var B = (function (_super) {
__extends(B, _super);
function B() {
_super.apply(this, arguments);
}
return B;
}(A));
a_1.a.f();
}
};
//// [a.js]
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var b_1 = require('./b');
exports.a = {
f: function () {
var A = (function () {
function A() {
}
return A;
}());
var B = (function (_super) {
__extends(B, _super);
function B() {
_super.apply(this, arguments);
}
return B;
}(A));
b_1.b.f();
}
};
@@ -0,0 +1,46 @@
=== tests/cases/compiler/a.ts ===
import { b } from './b';
>b : Symbol(b, Decl(a.ts, 0, 8))
export const a = {
>a : Symbol(a, Decl(a.ts, 1, 12))
f: () => {
>f : Symbol(f, Decl(a.ts, 1, 18))
class A { }
>A : Symbol(A, Decl(a.ts, 2, 14))
class B extends A { }
>B : Symbol(B, Decl(a.ts, 3, 19))
>A : Symbol(A, Decl(a.ts, 2, 14))
b.f();
>b.f : Symbol(f, Decl(b.ts, 1, 18))
>b : Symbol(b, Decl(a.ts, 0, 8))
>f : Symbol(f, Decl(b.ts, 1, 18))
}
};
=== tests/cases/compiler/b.ts ===
import { a } from './a';
>a : Symbol(a, Decl(b.ts, 0, 8))
export const b = {
>b : Symbol(b, Decl(b.ts, 1, 12))
f: () => {
>f : Symbol(f, Decl(b.ts, 1, 18))
class A { }
>A : Symbol(A, Decl(b.ts, 2, 14))
class B extends A { }
>B : Symbol(B, Decl(b.ts, 3, 19))
>A : Symbol(A, Decl(b.ts, 2, 14))
a.f();
>a.f : Symbol(f, Decl(a.ts, 1, 18))
>a : Symbol(a, Decl(b.ts, 0, 8))
>f : Symbol(f, Decl(a.ts, 1, 18))
}
};
@@ -0,0 +1,52 @@
=== tests/cases/compiler/a.ts ===
import { b } from './b';
>b : { f: () => void; }
export const a = {
>a : { f: () => void; }
>{ f: () => { class A { } class B extends A { } b.f(); }} : { f: () => void; }
f: () => {
>f : () => void
>() => { class A { } class B extends A { } b.f(); } : () => void
class A { }
>A : A
class B extends A { }
>B : B
>A : A
b.f();
>b.f() : void
>b.f : () => void
>b : { f: () => void; }
>f : () => void
}
};
=== tests/cases/compiler/b.ts ===
import { a } from './a';
>a : { f: () => void; }
export const b = {
>b : { f: () => void; }
>{ f: () => { class A { } class B extends A { } a.f(); }} : { f: () => void; }
f: () => {
>f : () => void
>() => { class A { } class B extends A { } a.f(); } : () => void
class A { }
>A : A
class B extends A { }
>B : B
>A : A
a.f();
>a.f() : void
>a.f : () => void
>a : { f: () => void; }
>f : () => void
}
};
@@ -0,0 +1,15 @@
// @target: es6
// @filename: a.ts
import { b } from './b';
export const a = {
f: async () => {
await b.f();
}
};
// @filename: b.ts
import { a } from './a';
export const b = {
f: async () => {
await a.f();
}
};
@@ -0,0 +1,20 @@
// @target: es5
// @module: commonjs
// @filename: a.ts
import { b } from './b';
export const a = {
f: () => {
class A { }
class B extends A { }
b.f();
}
};
// @filename: b.ts
import { a } from './a';
export const b = {
f: () => {
class A { }
class B extends A { }
a.f();
}
};