mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
Compare commits
1 Commits
19.0.0-next.6
...
v10.2.5
| Author | SHA1 | Date | |
|---|---|---|---|
| c270c88dfd |
@@ -127,6 +127,10 @@ class TraceDynamicValueVisitor implements DynamicValueVisitor<ts.DiagnosticRelat
|
||||
return [makeRelatedInformation(value.node, 'Unknown reference.')];
|
||||
}
|
||||
|
||||
visitDynamicType(value: DynamicValue): ts.DiagnosticRelatedInformation[] {
|
||||
return [makeRelatedInformation(value.node, 'Dynamic type.')];
|
||||
}
|
||||
|
||||
visitUnsupportedSyntax(value: DynamicValue): ts.DiagnosticRelatedInformation[] {
|
||||
return [makeRelatedInformation(value.node, 'This syntax is not supported.')];
|
||||
}
|
||||
|
||||
@@ -61,6 +61,11 @@ export const enum DynamicValueReason {
|
||||
*/
|
||||
COMPLEX_FUNCTION_CALL,
|
||||
|
||||
/**
|
||||
* A type that could not be statically evaluated.
|
||||
*/
|
||||
DYNAMIC_TYPE,
|
||||
|
||||
/**
|
||||
* A value could not be determined statically for any reason other the above.
|
||||
*/
|
||||
@@ -104,6 +109,10 @@ export class DynamicValue<R = unknown> {
|
||||
return new DynamicValue(node, fn, DynamicValueReason.COMPLEX_FUNCTION_CALL);
|
||||
}
|
||||
|
||||
static fromDynamicType(node: ts.TypeNode): DynamicValue {
|
||||
return new DynamicValue(node, undefined, DynamicValueReason.DYNAMIC_TYPE);
|
||||
}
|
||||
|
||||
static fromUnknown(node: ts.Node): DynamicValue {
|
||||
return new DynamicValue(node, undefined, DynamicValueReason.UNKNOWN);
|
||||
}
|
||||
@@ -136,6 +145,10 @@ export class DynamicValue<R = unknown> {
|
||||
return this.code === DynamicValueReason.COMPLEX_FUNCTION_CALL;
|
||||
}
|
||||
|
||||
isFromDynamicType(this: DynamicValue<R>): this is DynamicValue {
|
||||
return this.code === DynamicValueReason.DYNAMIC_TYPE;
|
||||
}
|
||||
|
||||
isFromUnknown(this: DynamicValue<R>): this is DynamicValue {
|
||||
return this.code === DynamicValueReason.UNKNOWN;
|
||||
}
|
||||
@@ -158,6 +171,8 @@ export class DynamicValue<R = unknown> {
|
||||
case DynamicValueReason.COMPLEX_FUNCTION_CALL:
|
||||
return visitor.visitComplexFunctionCall(
|
||||
this as unknown as DynamicValue<FunctionDefinition>);
|
||||
case DynamicValueReason.DYNAMIC_TYPE:
|
||||
return visitor.visitDynamicType(this);
|
||||
case DynamicValueReason.UNKNOWN:
|
||||
return visitor.visitUnknown(this);
|
||||
}
|
||||
@@ -172,5 +187,6 @@ export interface DynamicValueVisitor<R> {
|
||||
visitUnknownIdentifier(value: DynamicValue): R;
|
||||
visitInvalidExpressionType(value: DynamicValue): R;
|
||||
visitComplexFunctionCall(value: DynamicValue<FunctionDefinition>): R;
|
||||
visitDynamicType(value: DynamicValue): R;
|
||||
visitUnknown(value: DynamicValue): R;
|
||||
}
|
||||
|
||||
@@ -276,12 +276,19 @@ export class StaticInterpreter {
|
||||
return this.getReference(node, context);
|
||||
}
|
||||
}
|
||||
|
||||
private visitVariableDeclaration(node: ts.VariableDeclaration, context: Context): ResolvedValue {
|
||||
const value = this.host.getVariableValue(node);
|
||||
if (value !== null) {
|
||||
return this.visitExpression(value, context);
|
||||
} else if (isVariableDeclarationDeclared(node)) {
|
||||
if (node.type !== undefined) {
|
||||
const evaluatedType = this.visitType(node.type, context);
|
||||
if (!(evaluatedType instanceof DynamicValue)) {
|
||||
return evaluatedType;
|
||||
}
|
||||
}
|
||||
// fallback to returning a reference if this variable declaration does not have a type or we
|
||||
// are unable to evaluate its type
|
||||
return this.getReference(node, context);
|
||||
} else {
|
||||
return undefined;
|
||||
@@ -681,6 +688,45 @@ export class StaticInterpreter {
|
||||
private getReference<T extends DeclarationNode>(node: T, context: Context): Reference<T> {
|
||||
return new Reference(node, owningModule(context));
|
||||
}
|
||||
|
||||
private visitType(node: ts.TypeNode, context: Context): ResolvedValue {
|
||||
if (ts.isLiteralTypeNode(node)) {
|
||||
return this.visitLiteralType(node, context);
|
||||
} else if (ts.isTupleTypeNode(node)) {
|
||||
return this.visitTupleType(node, context);
|
||||
} else if (ts.isNamedTupleMember(node)) {
|
||||
return this.visitType(node.type, context);
|
||||
}
|
||||
|
||||
return DynamicValue.fromDynamicType(node);
|
||||
}
|
||||
|
||||
private visitLiteralType(node: ts.LiteralTypeNode, context: Context): ResolvedValue {
|
||||
if (ts.isLiteralExpression(node.literal)) {
|
||||
return this.visitExpression(node.literal, context);
|
||||
}
|
||||
|
||||
switch (node.literal.kind) {
|
||||
case ts.SyntaxKind.TrueKeyword:
|
||||
return true;
|
||||
case ts.SyntaxKind.FalseKeyword:
|
||||
return false;
|
||||
case ts.SyntaxKind.NullKeyword:
|
||||
return null;
|
||||
case ts.SyntaxKind.PrefixUnaryExpression:
|
||||
return this.visitExpression(node.literal, context);
|
||||
}
|
||||
}
|
||||
|
||||
private visitTupleType(node: ts.TupleTypeNode, context: Context): ResolvedValueArray {
|
||||
const res: ResolvedValueArray = [];
|
||||
|
||||
for (const elem of node.elements) {
|
||||
res.push(this.visitType(elem, context));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
function isFunctionOrMethodReference(ref: Reference<ts.Node>):
|
||||
|
||||
@@ -357,6 +357,35 @@ runInEachFileSystem(() => {
|
||||
expect(value.reason.node.getText()).toEqual('window: any');
|
||||
});
|
||||
|
||||
it('supports declarations of primitive constant types', () => {
|
||||
expect(evaluate(`declare const x: 'foo';`, `x`)).toEqual('foo');
|
||||
expect(evaluate(`declare const x: 42;`, `x`)).toEqual(42);
|
||||
expect(evaluate(`declare const x: null;`, `x`)).toEqual(null);
|
||||
expect(evaluate(`declare const x: true;`, `x`)).toEqual(true);
|
||||
});
|
||||
|
||||
it('supports declarations of tuples', () => {
|
||||
expect(evaluate(`declare const x: ['foo', 42, null, true];`, `x`)).toEqual([
|
||||
'foo', 42, null, true
|
||||
]);
|
||||
expect(evaluate(`declare const x: ['bar'];`, `[...x]`)).toEqual(['bar']);
|
||||
});
|
||||
|
||||
it('evaluates tuple elements it cannot understand to DynamicValue', () => {
|
||||
const value = evaluate(`declare const x: ['foo', string];`, `x`);
|
||||
|
||||
if (!Array.isArray(value)) {
|
||||
return fail('expected an array');
|
||||
}
|
||||
expect(value[0]).toEqual('foo');
|
||||
|
||||
if (!(value[1] instanceof DynamicValue)) {
|
||||
return fail('expected a DynamicValue when evaluating string element');
|
||||
}
|
||||
expect(value[1].isFromDynamicType()).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
it('imports work', () => {
|
||||
const {program} = makeProgram([
|
||||
{name: _('/second.ts'), contents: 'export function foo(bar) { return bar; }'},
|
||||
|
||||
Reference in New Issue
Block a user