Permalink
Browse files

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
  • Loading branch information...
alxhub authored and IgorMinar committed Aug 29, 2018
1 parent 13ccdfd commit 96d6b79ada29d28bed7d139115957b9812ce5961
@@ -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);
}
@@ -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);
});
});

0 comments on commit 96d6b79

Please sign in to comment.