TypeScript Version: 2.0.5
Code
// A.ts
namespace ts {
export function printVersion():void {
console.log("Version: " + sys.version);
}
}
// B.ts
namespace ts {
export let sys:{version:string} = {version: "2.0.5"};
ts.printVersion();
}
// tsconfig.json
{
"compilerOptions": {
"target": "es5",
"outFile" : "test.js"
},
"files": [
"A.ts",
"B.ts"
]
}
When I try to compile these two simple files (A.ts and B.ts) into one test.js file. The compiler reports an error:
A.ts(4,35): error TS2448: Block-scoped variable 'sys' used before its declaration.
But if I merge these two files into one like this:
// Single.ts
namespace ts {
export function printVersion():void {
console.log("Version: " + sys.version);
}
export let sys:{version:string} = {version: "2.0.5"};
ts.printVersion();
}
The compiler does not report this error.
Obviously the usage of sys.version is deferred, because it is inside a function. So it should be legal even if the usage and declaration are in separate files. Then I checked the source code of TypeScript, I think the code that caused this problem might be here:
src/compiler/checker.ts line:585
function isBlockScopedNameDeclaredBeforeUse(declaration: Declaration, usage: Node): boolean {
const declarationFile = getSourceFileOfNode(declaration);
const useFile = getSourceFileOfNode(usage);
if (declarationFile !== useFile) {
if ((modulekind && (declarationFile.externalModuleIndicator || useFile.externalModuleIndicator)) ||
(!compilerOptions.outFile && !compilerOptions.out)) {
// nodes are in different files and order cannot be determines
return true;
}
const sourceFiles = host.getSourceFiles();
return indexOf(sourceFiles, declarationFile) <= indexOf(sourceFiles, useFile);
}
if (declaration.pos <= usage.pos) {
// declaration is before usage
// still might be illegal if usage is in the initializer of the variable declaration
return declaration.kind !== SyntaxKind.VariableDeclaration ||
!isImmediatelyUsedInInitializerOfBlockScopedVariable(<VariableDeclaration>declaration, usage);
}
// declaration is after usage
// can be legal if usage is deferred (i.e. inside function or in initializer of instance property)
return isUsedInFunctionOrNonStaticProperty(declaration, usage);
}
In the isBlockScopedNameDeclaredBeforeUse function , if the declarationFile and useFile are not the same, it forgets to check whether the usage is deferred.
TypeScript Version: 2.0.5
Code
When I try to compile these two simple files (A.ts and B.ts) into one test.js file. The compiler reports an error:
But if I merge these two files into one like this:
The compiler does not report this error.
Obviously the usage of
sys.versionis deferred, because it is inside a function. So it should be legal even if the usage and declaration are in separate files. Then I checked the source code of TypeScript, I think the code that caused this problem might be here:src/compiler/checker.ts line:585
In the
isBlockScopedNameDeclaredBeforeUsefunction , if thedeclarationFileanduseFileare not the same, it forgets to check whether the usage is deferred.