From 9f5288dad3ea3cfdf267e2cf3557a555825e9c7b Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Fri, 22 Mar 2019 17:18:47 -0700 Subject: [PATCH] feat(ivy): type-checking for some previously unsupported expressions (#29698) This commit adds support for the generation of type-checking expressions for forms which were previously unsupported: * array literals * map literals * keyed property accesses * non-null assertions Testing strategy: TCB tests included. Fixes #29327 FW-1218 #resolve PR Close #29698 --- .../src/ngtsc/typecheck/src/expression.ts | 18 ++++++++++++++++- .../typecheck/test/type_check_block_spec.ts | 20 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts index 960fa4684f..32d7946a61 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, ASTWithSource, Binary, Conditional, Interpolation, LiteralPrimitive, MethodCall, PropertyRead} from '@angular/compiler'; +import {AST, ASTWithSource, Binary, Conditional, Interpolation, KeyedRead, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, NonNullAssert, PropertyRead} from '@angular/compiler'; import * as ts from 'typescript'; const BINARY_OPS = new Map([ @@ -76,6 +76,22 @@ export function astToTypescript( const trueExpr = astToTypescript(ast.trueExp, maybeResolve); const falseExpr = astToTypescript(ast.falseExp, maybeResolve); return ts.createParen(ts.createConditional(condExpr, trueExpr, falseExpr)); + } else if (ast instanceof LiteralArray) { + const elements = ast.expressions.map(expr => astToTypescript(expr, maybeResolve)); + return ts.createArrayLiteral(elements); + } else if (ast instanceof LiteralMap) { + const properties = ast.keys.map(({key}, idx) => { + const value = astToTypescript(ast.values[idx], maybeResolve); + return ts.createPropertyAssignment(ts.createStringLiteral(key), value); + }); + return ts.createObjectLiteral(properties, true); + } else if (ast instanceof KeyedRead) { + const receiver = astToTypescript(ast.obj, maybeResolve); + const key = astToTypescript(ast.key, maybeResolve); + return ts.createElementAccess(receiver, key); + } else if (ast instanceof NonNullAssert) { + const expr = astToTypescript(ast.expression, maybeResolve); + return ts.createNonNullExpression(expr); } else { throw new Error(`Unknown node type: ${Object.getPrototypeOf(ast).constructor}`); } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts index 170c532f71..d4320be88d 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts @@ -19,6 +19,26 @@ import {generateTypeCheckBlock} from '../src/type_check_block'; describe('type check blocks', () => { it('should generate a basic block for a binding', () => { expect(tcb('{{hello}}')).toContain('ctx.hello;'); }); + + it('should generate literal map expressions', () => { + const TEMPLATE = '{{ method({foo: a, bar: b}) }}'; + expect(tcb(TEMPLATE)).toContain('ctx.method({ "foo": ctx.a, "bar": ctx.b });'); + }); + + it('should generate literal array expressions', () => { + const TEMPLATE = '{{ method([a, b]) }}'; + expect(tcb(TEMPLATE)).toContain('ctx.method([ctx.a, ctx.b]);'); + }); + + it('should handle non-null assertions', () => { + const TEMPLATE = `{{a!}}`; + expect(tcb(TEMPLATE)).toContain('ctx.a!;'); + }); + + it('should handle keyed property access', () => { + const TEMPLATE = `{{a[b]}}`; + expect(tcb(TEMPLATE)).toContain('ctx.a[ctx.b];'); + }); }); function getClass(sf: ts.SourceFile, name: string): ClassDeclaration {