feat(ivy): resolve references to vars in .d.ts files (#25775)

Previously, if ngtsc encountered a VariableDeclaration without an
initializer, it would assume that the variable was undefined, and
return that result.

However, for symbols exported from external modules that resolve to
.d.ts files, variable declarations are of the form:

export declare let varName: Type;

This form also lacks an initializer, but indicates the presence of an
importable symbol which can be referenced. This commit changes the
static resolver to understand variable declarations with the 'declare'
keyword and to generate references when it encounters them.

PR Close #25775
This commit is contained in:
Alex Rickabaugh 2018-08-29 12:50:00 -07:00 committed by Igor Minar
parent 13ccdfd89d
commit 96d6b79ada
2 changed files with 37 additions and 4 deletions

View File

@ -439,10 +439,7 @@ class StaticInterpreter {
if (this.host.isClass(node)) {
return this.getReference(node, context);
} else if (ts.isVariableDeclaration(node)) {
if (!node.initializer) {
return undefined;
}
return this.visitExpression(node.initializer, context);
return this.visitVariableDeclaration(node, context);
} else if (ts.isParameter(node) && context.scope.has(node)) {
return context.scope.get(node) !;
} else if (ts.isExportAssignment(node)) {
@ -456,6 +453,16 @@ class StaticInterpreter {
}
}
private visitVariableDeclaration(node: ts.VariableDeclaration, context: Context): ResolvedValue {
if (node.initializer !== undefined) {
return this.visitExpression(node.initializer, context);
} else if (isVariableDeclarationDeclared(node)) {
return this.getReference(node, context);
} else {
return undefined;
}
}
private visitEnumDeclaration(node: ts.EnumDeclaration, context: Context): ResolvedValue {
const enumRef = this.getReference(node, context) as Reference<ts.EnumDeclaration>;
const map = new Map<string, EnumValue>();
@ -728,3 +735,16 @@ function identifierOfDeclaration(decl: ts.Declaration): ts.Identifier|undefined
function isPossibleClassDeclaration(node: ts.Node): node is ts.Declaration {
return ts.isClassDeclaration(node) || ts.isVariableDeclaration(node);
}
function isVariableDeclarationDeclared(node: ts.VariableDeclaration): boolean {
if (node.parent === undefined || !ts.isVariableDeclarationList(node.parent)) {
return false;
}
const declList = node.parent;
if (declList.parent === undefined || !ts.isVariableStatement(declList.parent)) {
return false;
}
const varStmt = declList.parent;
return varStmt.modifiers !== undefined &&
varStmt.modifiers.some(mod => mod.kind === ts.SyntaxKind.DeclareKeyword);
}

View File

@ -284,4 +284,17 @@ describe('ngtsc metadata', () => {
expect(result.enumRef.node.name.text).toBe('Foo');
expect(result.name).toBe('B');
});
it('variable declaration resolution works', () => {
const {program} = makeProgram([
{name: 'decl.d.ts', contents: 'export declare let value: number;'},
{name: 'entry.ts', contents: `import {value} from './decl'; const target$ = value;`},
]);
const checker = program.getTypeChecker();
const host = new TypeScriptReflectionHost(checker);
const result = getDeclaration(program, 'entry.ts', 'target$', ts.isVariableDeclaration);
const res = staticallyResolve(result.initializer !, host, checker);
console.error(res);
expect(res instanceof Reference).toBe(true);
});
});