fix(ivy): evaluate external declaration usages as dynamic (#30247)

Previously, ngtsc would fail to evaluate expressions that access properties
from e.g. the `window` object. This resulted in hard to debug error messages
as no indication on where the problem originated was present in the output.

This commit cleans up the handling of unknown property accesses, such that
evaluating such expressions no longer fail but instead result in a `DynamicValue`.

Fixes #30226

PR Close #30247
This commit is contained in:
JoostK 2019-05-02 22:58:07 +02:00 committed by Jason Aden
parent 3f7e823b80
commit 1b613c3ebf
3 changed files with 22 additions and 3 deletions

View File

@ -32,7 +32,8 @@ export const enum DynamicValueReason {
/**
* An external reference could not be resolved to a value which can be evaluated.
* (E.g. a call expression for a function declared in `.d.ts`.)
* For example a call expression for a function declared in `.d.ts`, or accessing native globals
* such as `window`.
*/
EXTERNAL_REFERENCE,

View File

@ -11,6 +11,7 @@ import * as ts from 'typescript';
import {Reference} from '../../imports';
import {OwningModule} from '../../imports/src/references';
import {Declaration, ReflectionHost} from '../../reflection';
import {isDeclaration} from '../../util/src/typescript';
import {ArrayConcatBuiltinFn, ArraySliceBuiltinFn} from './builtin';
import {DynamicValue} from './dynamic';
@ -361,12 +362,15 @@ export class StaticInterpreter {
}
}
return value;
} else if (isDeclaration(ref)) {
return DynamicValue.fromDynamicInput(
node, DynamicValue.fromExternalReference(ref, lhs as Reference<ts.Declaration>));
}
} else if (lhs instanceof DynamicValue) {
return DynamicValue.fromDynamicInput(node, lhs);
} else {
throw new Error(`Invalid dot property access: ${lhs} dot ${rhs}`);
}
return DynamicValue.fromUnknown(node);
}
private visitCallExpression(node: ts.CallExpression, context: Context): ResolvedValue {

View File

@ -122,6 +122,20 @@ describe('ngtsc metadata', () => {
expect(evaluate(`const x = 3;`, '!!x')).toEqual(true);
});
it('resolves access from external variable declarations as dynamic value', () => {
const value = evaluate('declare const window: any;', 'window.location');
if (!(value instanceof DynamicValue)) {
return fail(`Should have resolved to a DynamicValue`);
}
expect(value.isFromDynamicInput()).toEqual(true);
expect(value.node.getText()).toEqual('window.location');
if (!(value.reason instanceof DynamicValue)) {
return fail(`Should have a DynamicValue as reason`);
}
expect(value.reason.isFromExternalReference()).toEqual(true);
expect(value.reason.node.getText()).toEqual('window: any');
});
it('imports work', () => {
const {program} = makeProgram([
{name: 'second.ts', contents: 'export function foo(bar) { return bar; }'},