refactor(ivy): correctly type class declarations in ngtsc/ngcc (#29209)

Previously, several `ngtsc` and `ngcc` APIs dealing with class
declaration nodes used inconsistent types. For example, some methods of
the `DecoratorHandler` interface expected a `ts.Declaration` argument,
but actual `DecoratorHandler` implementations specified a stricter
`ts.ClassDeclaration` type.

As a result, the stricter methods would operate under the incorrect
assumption that their arguments were of type `ts.ClassDeclaration`,
while the actual arguments might be of different types (e.g. `ngcc`
would call them with `ts.FunctionDeclaration` or
`ts.VariableDeclaration` arguments, when compiling ES5 code).

Additionally, since we need those class declarations to be referenced in
other parts of the program, `ngtsc`/`ngcc` had to either repeatedly
check for `ts.isIdentifier(node.name)` or assume there was a `name`
identifier and use `node.name!`. While this assumption happens to be
true in the current implementation, working around type-checking is
error-prone (e.g. the assumption might stop being true in the future).

This commit fixes this by introducing a new type to be used for such
class declarations (`ts.Declaration & {name: ts.Identifier}`) and using
it consistently throughput the code.

PR Close #29209
This commit is contained in:
George Kalpakas
2019-03-20 12:10:57 +02:00
committed by Miško Hevery
parent 2d859a8c3a
commit bb6a3632f6
36 changed files with 229 additions and 194 deletions

View File

@ -11,6 +11,7 @@ ts_library(
"//packages:types",
"//packages/compiler",
"//packages/compiler-cli/src/ngtsc/path",
"//packages/compiler-cli/src/ngtsc/reflection",
"//packages/compiler-cli/src/ngtsc/util",
"@npm//@types/node",
"@npm//typescript",

View File

@ -9,6 +9,7 @@
import {Expression, ExternalExpr} from '@angular/compiler';
import * as ts from 'typescript';
import {ClassDeclaration} from '../../reflection';
import {FileToModuleHost, ReferenceEmitStrategy} from './emitter';
import {ImportMode, Reference} from './references';
@ -18,20 +19,16 @@ const CHARS_TO_ESCAPE = /[^a-zA-Z0-9/_]/g;
export class AliasGenerator {
constructor(private fileToModuleHost: FileToModuleHost) {}
aliasSymbolName(decl: ts.Declaration, context: ts.SourceFile): string {
if (!ts.isClassDeclaration(decl)) {
throw new Error(`Attempt to write an alias to something which isn't a class`);
}
aliasSymbolName(decl: ClassDeclaration, context: ts.SourceFile): string {
// The declared module is used to get the name of the alias.
const declModule =
this.fileToModuleHost.fileNameToModuleName(decl.getSourceFile().fileName, context.fileName);
const replaced = declModule.replace(CHARS_TO_ESCAPE, '_').replace(/\//g, '$');
return 'ɵng$' + replaced + '$$' + decl.name !.text;
return 'ɵng$' + replaced + '$$' + decl.name.text;
}
aliasTo(decl: ts.Declaration, via: ts.SourceFile): Expression {
aliasTo(decl: ClassDeclaration, via: ts.SourceFile): Expression {
const name = this.aliasSymbolName(decl, via);
// viaModule is the module it'll actually be imported from.
const moduleName = this.fileToModuleHost.fileNameToModuleName(via.fileName, via.fileName);