feat(compiler-cli): add support for TypeScript 4.0 (#38076)

With this change we add support for TypeScript 4.0

PR Close #38076
This commit is contained in:
Alan Agius
2020-07-15 12:21:04 +02:00
committed by Misko Hevery
parent 201a546af8
commit 0fc44e0436
37 changed files with 315 additions and 141 deletions

View File

@ -27,7 +27,7 @@
},
"peerDependencies": {
"@angular/compiler": "0.0.0-PLACEHOLDER",
"typescript": ">=3.9 <4.0"
"typescript": ">=3.9 <4.1"
},
"engines": {
"node": ">=10.0"

View File

@ -476,13 +476,16 @@ export class Evaluator {
return recordEntry(typeReference, node);
case ts.SyntaxKind.UnionType:
const unionType = <ts.UnionTypeNode>node;
// Remove null and undefined from the list of unions.
const references = unionType.types
.filter(
n => n.kind != ts.SyntaxKind.NullKeyword &&
n.kind != ts.SyntaxKind.UndefinedKeyword)
.map(n => this.evaluateNode(n));
// TODO(alan-agius4): remove `n.kind !== ts.SyntaxKind.NullKeyword` when
// TS 3.9 support is dropped. In TS 4.0 NullKeyword is a child of LiteralType.
const references =
unionType.types
.filter(
n => n.kind !== ts.SyntaxKind.NullKeyword &&
n.kind !== ts.SyntaxKind.UndefinedKeyword &&
!(ts.isLiteralTypeNode(n) && n.literal.kind === ts.SyntaxKind.NullKeyword))
.map(n => this.evaluateNode(n));
// The remmaining reference must be the same. If two have type arguments consider them
// different even if the type arguments are the same.

View File

@ -62,8 +62,8 @@ export class Symbols {
// even if the `SourceFile` was not type checked (which looks for `SourceFile`
// in the parent chain). This doesn't damage the node as the binder unconditionally
// sets the parent.
externalReference.expression.parent = externalReference;
externalReference.parent = this.sourceFile as any;
(externalReference.expression.parent as ts.Node) = externalReference;
(externalReference.parent as ts.Node) = this.sourceFile;
}
const from = stripQuotes(externalReference.expression.getText());
symbols.set(
@ -83,8 +83,8 @@ export class Symbols {
}
if (!importDecl.moduleSpecifier.parent) {
// See note above in the `ImportEqualDeclaration` case.
importDecl.moduleSpecifier.parent = importDecl;
importDecl.parent = this.sourceFile;
(importDecl.moduleSpecifier.parent as ts.Node) = importDecl;
(importDecl.parent as ts.Node) = this.sourceFile;
}
const from = stripQuotes(importDecl.moduleSpecifier.getText());
if (importDecl.importClause.name) {

View File

@ -20,22 +20,26 @@ export function extractReferencesFromType(
if (!ts.isTupleTypeNode(def)) {
return [];
}
return def.elementTypes.map(element => {
if (!ts.isTypeQueryNode(element)) {
throw new Error(`Expected TypeQueryNode: ${nodeDebugInfo(element)}`);
}
const type = element.exprName;
const {node, from} = reflectTypeEntityToDeclaration(type, checker);
if (!isNamedClassDeclaration(node)) {
throw new Error(`Expected named ClassDeclaration: ${nodeDebugInfo(node)}`);
}
const specifier = (from !== null && !from.startsWith('.') ? from : ngModuleImportedFrom);
if (specifier !== null) {
return new Reference(node, {specifier, resolutionContext});
} else {
return new Reference(node);
}
});
// TODO(alan-agius4): remove `def.elementTypes` and casts when TS 3.9 support is dropped and G3 is
// using TS 4.0.
return (((def as any).elements || (def as any).elementTypes) as ts.NodeArray<ts.TypeNode>)
.map(element => {
if (!ts.isTypeQueryNode(element)) {
throw new Error(`Expected TypeQueryNode: ${nodeDebugInfo(element)}`);
}
const type = element.exprName;
const {node, from} = reflectTypeEntityToDeclaration(type, checker);
if (!isNamedClassDeclaration(node)) {
throw new Error(`Expected named ClassDeclaration: ${nodeDebugInfo(node)}`);
}
const specifier = (from !== null && !from.startsWith('.') ? from : ngModuleImportedFrom);
if (specifier !== null) {
return new Reference(node, {specifier, resolutionContext});
} else {
return new Reference(node);
}
});
}
export function readStringType(type: ts.TypeNode): string|null {
@ -69,12 +73,15 @@ export function readStringArrayType(type: ts.TypeNode): string[] {
return [];
}
const res: string[] = [];
type.elementTypes.forEach(el => {
if (!ts.isLiteralTypeNode(el) || !ts.isStringLiteral(el.literal)) {
return;
}
res.push(el.literal.text);
});
// TODO(alan-agius4): remove `def.elementTypes` and casts when TS 3.9 support is dropped and G3 is
// using TS 4.0.
(((type as any).elements || (type as any).elementTypes) as ts.NodeArray<ts.TypeNode>)
.forEach(el => {
if (!ts.isLiteralTypeNode(el) || !ts.isStringLiteral(el.literal)) {
return;
}
res.push(el.literal.text);
});
return res;
}

View File

@ -64,7 +64,11 @@ export class TypeScriptReflectionHost implements ReflectionHost {
// optional tokes that don't have providers.
if (typeNode && ts.isUnionTypeNode(typeNode)) {
let childTypeNodes = typeNode.types.filter(
childTypeNode => childTypeNode.kind !== ts.SyntaxKind.NullKeyword);
// TODO(alan-agius4): remove `childTypeNode.kind !== ts.SyntaxKind.NullKeyword` when
// TS 3.9 support is dropped. In TS 4.0 NullKeyword is a child of LiteralType.
childTypeNode => childTypeNode.kind !== ts.SyntaxKind.NullKeyword &&
!(ts.isLiteralTypeNode(childTypeNode) &&
childTypeNode.literal.kind === ts.SyntaxKind.NullKeyword));
if (childTypeNodes.length === 1) {
typeNode = childTypeNodes[0];

View File

@ -128,8 +128,6 @@ function transformFactorySourceFile(
const {moduleSymbols, sourceFilePath} = factoryMap.get(file.fileName)!;
file = ts.getMutableClone(file);
// Not every exported factory statement is valid. They were generated before the program was
// analyzed, and before ngtsc knew which symbols were actually NgModules. factoryMap contains
// that knowledge now, so this transform filters the statement list and removes exported factories
@ -221,7 +219,8 @@ function transformFactorySourceFile(
// satisfy closure compiler.
transformedStatements.push(nonEmptyExport);
}
file.statements = ts.createNodeArray(transformedStatements);
file = ts.updateSourceFileNode(file, transformedStatements);
// If any imports to @angular/core were detected and rewritten (which happens when compiling
// @angular/core), go through the SourceFile and rewrite references to symbols imported from core.

View File

@ -42,8 +42,7 @@ function flipIvySwitchInFile(sf: ts.SourceFile): ts.SourceFile {
// Only update the statements in the SourceFile if any have changed.
if (newStatements !== undefined) {
sf = ts.getMutableClone(sf);
sf.statements = ts.createNodeArray(newStatements);
return ts.updateSourceFileNode(sf, newStatements);
}
return sf;
}
@ -105,7 +104,7 @@ function flipIvySwitchesInVariableStatement(
// Find the post-switch variable identifier. If one can't be found, it's an error. This is
// reported as a thrown error and not a diagnostic as transformers cannot output diagnostics.
let newIdentifier = findPostSwitchIdentifier(statements, postSwitchName);
const newIdentifier = findPostSwitchIdentifier(statements, postSwitchName);
if (newIdentifier === null) {
throw new Error(`Unable to find identifier ${postSwitchName} in ${
stmt.getSourceFile().fileName} for the Ivy switch.`);

View File

@ -28,9 +28,7 @@ export function aliasTransformFactory(exportStatements: Map<string, Map<string,
statements.push(stmt);
});
file = ts.getMutableClone(file);
file.statements = ts.createNodeArray(statements);
return file;
return ts.updateSourceFileNode(file, statements);
};
};
}

View File

@ -239,32 +239,48 @@ export class ReturnTypeTransform implements DtsTransform {
}
transformClassElement(element: ts.ClassElement, imports: ImportManager): ts.ClassElement {
if (!ts.isMethodSignature(element)) {
return element;
// // TODO(alan-agius4): Remove when we no longer support TS 3.9
// TS <= 3.9
if (ts.isMethodSignature(element)) {
const original = ts.getOriginalNode(element) as ts.MethodDeclaration;
if (!this.typeReplacements.has(original)) {
return element;
}
const returnType = this.typeReplacements.get(original)!;
const tsReturnType = translateType(returnType, imports);
const methodSignature = ts.updateMethodSignature(
/* node */ element,
/* typeParameters */ element.typeParameters,
/* parameters */ element.parameters,
/* type */ tsReturnType,
/* name */ element.name,
/* questionToken */ element.questionToken);
// Copy over any modifiers, these cannot be set during the `ts.updateMethodSignature` call.
(methodSignature.modifiers as ts.ModifiersArray | undefined) = element.modifiers;
// A bug in the TypeScript declaration causes `ts.MethodSignature` not to be assignable to
// `ts.ClassElement`. Since `element` was a `ts.MethodSignature` already, transforming it into
// this type is actually correct.
return methodSignature as unknown as ts.ClassElement;
}
const original = ts.getOriginalNode(element) as ts.MethodDeclaration;
if (!this.typeReplacements.has(original)) {
return element;
// TS 4.0 +
if (ts.isMethodDeclaration(element)) {
const original = ts.getOriginalNode(element, ts.isMethodDeclaration);
if (!this.typeReplacements.has(original)) {
return element;
}
const returnType = this.typeReplacements.get(original)!;
const tsReturnType = translateType(returnType, imports);
return ts.updateMethod(
element, element.decorators, element.modifiers, element.asteriskToken, element.name,
element.questionToken, element.typeParameters, element.parameters, tsReturnType,
element.body);
}
const returnType = this.typeReplacements.get(original)!;
const tsReturnType = translateType(returnType, imports);
const methodSignature = ts.updateMethodSignature(
/* node */ element,
/* typeParameters */ element.typeParameters,
/* parameters */ element.parameters,
/* type */ tsReturnType,
/* name */ element.name,
/* questionToken */ element.questionToken);
// Copy over any modifiers, these cannot be set during the `ts.updateMethodSignature` call.
methodSignature.modifiers = element.modifiers;
// A bug in the TypeScript declaration causes `ts.MethodSignature` not to be assignable to
// `ts.ClassElement`. Since `element` was a `ts.MethodSignature` already, transforming it into
// this type is actually correct.
return methodSignature as unknown as ts.ClassElement;
return element;
}
transformFunctionDeclaration(element: ts.FunctionDeclaration, imports: ImportManager):

View File

@ -187,8 +187,8 @@ class IvyTransformationVisitor extends Visitor {
// Create a new `NodeArray` with the filtered decorators that sourcemaps back to the original.
const array = ts.createNodeArray(filtered);
array.pos = node.decorators.pos;
array.end = node.decorators.end;
(array.pos as number) = node.decorators.pos;
(array.end as number) = node.decorators.end;
return array;
}

View File

@ -40,8 +40,9 @@ export function addImports(
// for @fileoverview Closure annotation. If there is no @fileoverview annotations, this
// statement would be a noop.
const fileoverviewAnchorStmt = ts.createNotEmittedStatement(sf);
sf.statements = ts.createNodeArray(
[fileoverviewAnchorStmt, ...existingImports, ...addedImports, ...extraStatements, ...body]);
return ts.updateSourceFileNode(sf, ts.createNodeArray([
fileoverviewAnchorStmt, ...existingImports, ...addedImports, ...extraStatements, ...body
]));
}
return sf;

View File

@ -526,7 +526,11 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor {
visitLiteralExpr(ast: LiteralExpr, context: Context): ts.TypeNode {
if (ast.value === null) {
return ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword);
// TODO(alan-agius4): Remove when we no longer support TS 3.9
// Use: return ts.createLiteralTypeNode(ts.createNull()) directly.
return ts.versionMajorMinor.charAt(0) === '4' ?
ts.createLiteralTypeNode(ts.createNull() as any) :
ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword as any);
} else if (ast.value === undefined) {
return ts.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword);
} else if (typeof ast.value === 'boolean') {
@ -696,7 +700,7 @@ function createLocalizedStringTaggedTemplate(
// Revert once https://github.com/microsoft/TypeScript/issues/35374 is fixed
function createTemplateMiddle(cooked: string, raw: string): ts.TemplateMiddle {
const node: ts.TemplateLiteralLikeNode = ts.createTemplateHead(cooked, raw);
node.kind = ts.SyntaxKind.TemplateMiddle;
(node.kind as ts.SyntaxKind) = ts.SyntaxKind.TemplateMiddle;
return node as ts.TemplateMiddle;
}
@ -704,7 +708,7 @@ function createTemplateMiddle(cooked: string, raw: string): ts.TemplateMiddle {
// Revert once https://github.com/microsoft/TypeScript/issues/35374 is fixed
function createTemplateTail(cooked: string, raw: string): ts.TemplateTail {
const node: ts.TemplateLiteralLikeNode = ts.createTemplateHead(cooked, raw);
node.kind = ts.SyntaxKind.TemplateTail;
(node.kind as ts.SyntaxKind) = ts.SyntaxKind.TemplateTail;
return node as ts.TemplateTail;
}

View File

@ -36,6 +36,7 @@ export function canEmitType(type: ts.TypeNode, resolver: TypeReferenceResolver):
visitTypeReferenceNode: type => canEmitTypeReference(type),
visitArrayTypeNode: type => canEmitTypeWorker(type.elementType),
visitKeywordType: () => true,
visitLiteralType: () => true,
visitOtherType: () => false,
});
}
@ -111,6 +112,7 @@ export class TypeEmitter {
visitTypeReferenceNode: type => this.emitTypeReference(type),
visitArrayTypeNode: type => ts.updateArrayTypeNode(type, this.emitType(type.elementType)),
visitKeywordType: type => type,
visitLiteralType: type => type,
visitOtherType: () => {
throw new Error('Unable to emit a complex type');
},
@ -159,6 +161,7 @@ interface TypeEmitterVisitor<R> {
visitTypeReferenceNode(type: ts.TypeReferenceNode): R;
visitArrayTypeNode(type: ts.ArrayTypeNode): R;
visitKeywordType(type: ts.KeywordTypeNode): R;
visitLiteralType(type: ts.LiteralTypeNode): R;
visitOtherType(type: ts.TypeNode): R;
}
@ -167,6 +170,8 @@ function visitTypeNode<R>(type: ts.TypeNode, visitor: TypeEmitterVisitor<R>): R
return visitor.visitTypeReferenceNode(type);
} else if (ts.isArrayTypeNode(type)) {
return visitor.visitArrayTypeNode(type);
} else if (ts.isLiteralTypeNode(type)) {
return visitor.visitLiteralType(type);
}
switch (type.kind) {

View File

@ -456,7 +456,7 @@ class TestComponent {
}`);
expect(messages).toEqual(
[`TestComponent.html(1, 15): Type '2' is not assignable to type 'string'.`]);
[`TestComponent.html(1, 15): Type 'number' is not assignable to type 'string'.`]);
});
});
});

View File

@ -140,10 +140,16 @@ function createCtorParametersClassPropertyType(): ts.TypeNode {
undefined),
])),
undefined));
return ts.createFunctionTypeNode(
undefined, [],
ts.createArrayTypeNode(
ts.createUnionTypeNode([ts.createTypeLiteralNode(typeElements), ts.createNull()])));
// TODO(alan-agius4): Remove when we no longer support TS 3.9
const nullLiteral = ts.createNull() as any;
const nullType = ts.versionMajorMinor.charAt(0) === '4' ?
ts.createLiteralTypeNode(nullLiteral as any) :
nullLiteral;
return ts.createFunctionTypeNode(undefined, [], ts.createArrayTypeNode(ts.createUnionTypeNode([
ts.createTypeLiteralNode(typeElements),
nullType,
])));
}
/**
@ -287,8 +293,13 @@ function typeReferenceToExpression(
// Ignore any generic types, just return the base type.
return entityNameToExpression(typeRef.typeName);
case ts.SyntaxKind.UnionType:
// TODO(alan-agius4): remove `t.kind !== ts.SyntaxKind.NullKeyword` when
// TS 3.9 support is dropped. In TS 4.0 NullKeyword is a child of LiteralType.
const childTypeNodes =
(node as ts.UnionTypeNode).types.filter(t => t.kind !== ts.SyntaxKind.NullKeyword);
(node as ts.UnionTypeNode)
.types.filter(
t => t.kind !== ts.SyntaxKind.NullKeyword &&
!(ts.isLiteralTypeNode(t) && t.literal.kind === ts.SyntaxKind.NullKeyword));
return childTypeNodes.length === 1 ?
typeReferenceToExpression(entityNameToExpression, childTypeNodes[0]) :
undefined;
@ -434,7 +445,7 @@ export function getDownlevelDecoratorsTransform(
const name = (element.name as ts.Identifier).text;
const mutable = ts.getMutableClone(element);
mutable.decorators = decoratorsToKeep.length ?
(mutable as any).decorators = decoratorsToKeep.length ?
ts.setTextRange(ts.createNodeArray(decoratorsToKeep), mutable.decorators) :
undefined;
return [name, mutable, toLower];
@ -551,8 +562,6 @@ export function getDownlevelDecoratorsTransform(
}
}
const newClassDeclaration = ts.getMutableClone(classDecl);
if (decoratorsToLower.length) {
newMembers.push(createDecoratorClassProperty(decoratorsToLower));
}
@ -567,12 +576,13 @@ export function getDownlevelDecoratorsTransform(
if (decoratedProperties.size) {
newMembers.push(createPropDecoratorsClassProperty(diagnostics, decoratedProperties));
}
newClassDeclaration.members = ts.setTextRange(
ts.createNodeArray(newMembers, newClassDeclaration.members.hasTrailingComma),
classDecl.members);
newClassDeclaration.decorators =
decoratorsToKeep.length ? ts.createNodeArray(decoratorsToKeep) : undefined;
return newClassDeclaration;
const members = ts.setTextRange(
ts.createNodeArray(newMembers, classDecl.members.hasTrailingComma), classDecl.members);
return ts.updateClassDeclaration(
classDecl, decoratorsToKeep.length ? decoratorsToKeep : undefined, classDecl.modifiers,
classDecl.name, classDecl.typeParameters, classDecl.heritageClauses, members);
}
/**

View File

@ -167,14 +167,13 @@ function transformSourceFile(
newStatements = tmpStatements;
}
// Note: We cannot use ts.updateSourcefile here as
// it does not work well with decorators.
// See https://github.com/Microsoft/TypeScript/issues/17384
const newSf = ts.getMutableClone(sourceFile);
const newSf = ts.updateSourceFileNode(
sourceFile, ts.setTextRange(ts.createNodeArray(newStatements), sourceFile.statements));
if (!(sourceFile.flags & ts.NodeFlags.Synthesized)) {
newSf.flags &= ~ts.NodeFlags.Synthesized;
(newSf.flags as ts.NodeFlags) &= ~ts.NodeFlags.Synthesized;
}
newSf.statements = ts.setTextRange(ts.createNodeArray(newStatements), sourceFile.statements);
return newSf;
}
@ -209,11 +208,6 @@ export interface RequestsMap {
getRequests(sourceFile: ts.SourceFile): RequestLocationMap;
}
interface MetadataAndLoweringRequests {
metadata: ModuleMetadata|undefined;
requests: RequestLocationMap;
}
function isEligibleForLowering(node: ts.Node|undefined): boolean {
if (node) {
switch (node.kind) {

View File

@ -19,7 +19,7 @@ const MIN_TS_VERSION = '3.9.2';
* ∀ supported typescript version v, v < MAX_TS_VERSION
* MAX_TS_VERSION is not considered as a supported TypeScript version
*/
const MAX_TS_VERSION = '4.0.0';
const MAX_TS_VERSION = '4.1.0';
/**
* The currently used version of TypeScript, which can be adjusted for testing purposes using

View File

@ -787,7 +787,7 @@ describe('ng type checker', () => {
it('should report an invalid call to a pipe', () => {
rejectOnlyWithFullTemplateTypeCheck(
'<div>{{"hello" | aPipe}}</div>',
`Argument of type '"hello"' is not assignable to parameter of type 'number'.`, '0:5');
`Argument of type 'string' is not assignable to parameter of type 'number'.`, '0:5');
});
it('should report an invalid property on an exportAs directive', () => {
rejectOnlyWithFullTemplateTypeCheck(

View File

@ -136,7 +136,7 @@ export declare class AnimationEvent {
const diags = env.driveDiagnostics();
expect(diags.length).toBe(1);
expect(diags[0].messageText).toEqual(`Type '"2"' is not assignable to type 'number'.`);
expect(diags[0].messageText).toEqual(`Type 'string' is not assignable to type 'number'.`);
// The reported error code should be in the TS error space, not a -99 "NG" code.
expect(diags[0].code).toBeGreaterThan(0);
});
@ -168,8 +168,8 @@ export declare class AnimationEvent {
const diags = env.driveDiagnostics();
expect(diags.length).toBe(2);
expect(diags[0].messageText).toEqual(`Type '"2"' is not assignable to type 'number'.`);
expect(diags[1].messageText).toEqual(`Type '"2"' is not assignable to type 'number'.`);
expect(diags[0].messageText).toEqual(`Type 'string' is not assignable to type 'number'.`);
expect(diags[1].messageText).toEqual(`Type 'string' is not assignable to type 'number'.`);
});
it('should support inputs and outputs with names that are not JavaScript identifiers', () => {
@ -204,7 +204,7 @@ export declare class AnimationEvent {
const diags = env.driveDiagnostics();
expect(diags.length).toBe(2);
expect(diags[0].messageText).toEqual(`Type '2' is not assignable to type 'string'.`);
expect(diags[0].messageText).toEqual(`Type 'number' is not assignable to type 'string'.`);
expect(diags[1].messageText)
.toEqual(`Argument of type 'string' is not assignable to parameter of type 'number'.`);
});
@ -380,7 +380,7 @@ export declare class AnimationEvent {
const diags = env.driveDiagnostics();
expect(diags.length).toBe(2);
expect(diags[0].messageText).toEqual(`Type '1' is not assignable to type 'string'.`);
expect(diags[0].messageText).toEqual(`Type 'number' is not assignable to type 'string'.`);
expect(diags[1].messageText)
.toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
});
@ -390,7 +390,7 @@ export declare class AnimationEvent {
const diags = env.driveDiagnostics();
expect(diags.length).toBe(2);
expect(diags[0].messageText).toEqual(`Type '1' is not assignable to type 'string'.`);
expect(diags[0].messageText).toEqual(`Type 'number' is not assignable to type 'string'.`);
expect(diags[1].messageText)
.toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
});
@ -716,8 +716,8 @@ export declare class AnimationEvent {
const diags = env.driveDiagnostics();
expect(diags.length).toBe(2);
expect(diags[0].messageText).toEqual(`Type '""' is not assignable to type 'boolean'.`);
expect(diags[1].messageText).toEqual(`Type '"3"' is not assignable to type 'number'.`);
expect(diags[0].messageText).toEqual(`Type 'string' is not assignable to type 'boolean'.`);
expect(diags[1].messageText).toEqual(`Type 'string' is not assignable to type 'number'.`);
});
it('should produce an error for text attributes when overall strictness is enabled', () => {
@ -725,8 +725,8 @@ export declare class AnimationEvent {
const diags = env.driveDiagnostics();
expect(diags.length).toBe(2);
expect(diags[0].messageText).toEqual(`Type '""' is not assignable to type 'boolean'.`);
expect(diags[1].messageText).toEqual(`Type '"3"' is not assignable to type 'number'.`);
expect(diags[0].messageText).toEqual(`Type 'string' is not assignable to type 'boolean'.`);
expect(diags[1].messageText).toEqual(`Type 'string' is not assignable to type 'number'.`);
});
it('should not produce an error for text attributes when not enabled', () => {
@ -1119,7 +1119,7 @@ export declare class AnimationEvent {
const allErrors = [
`'does_not_exist' does not exist on type '{ name: string; }'`,
`Expected 2 arguments, but got 3.`,
`Argument of type '"test"' is not assignable to parameter of type 'number'`,
`Argument of type 'string' is not assignable to parameter of type 'number'`,
`Argument of type '{ name: string; }' is not assignable to parameter of type 'unknown[]'`,
];
@ -1241,11 +1241,11 @@ export declare class AnimationEvent {
const diags = env.driveDiagnostics();
expect(diags.length).toBe(3);
expect(diags[0].messageText).toBe(`Type 'true' is not assignable to type 'number'.`);
expect(diags[0].messageText).toBe(`Type 'boolean' is not assignable to type 'number'.`);
expect(getSourceCodeForDiagnostic(diags[0])).toEqual('[fromAbstract]="true"');
expect(diags[1].messageText).toBe(`Type '3' is not assignable to type 'string'.`);
expect(diags[1].messageText).toBe(`Type 'number' is not assignable to type 'string'.`);
expect(getSourceCodeForDiagnostic(diags[1])).toEqual('[fromBase]="3"');
expect(diags[2].messageText).toBe(`Type '4' is not assignable to type 'boolean'.`);
expect(diags[2].messageText).toBe(`Type 'number' is not assignable to type 'boolean'.`);
expect(getSourceCodeForDiagnostic(diags[2])).toEqual('[fromChild]="4"');
});
@ -1298,11 +1298,11 @@ export declare class AnimationEvent {
const diags = env.driveDiagnostics();
expect(diags.length).toBe(3);
expect(diags[0].messageText).toBe(`Type 'true' is not assignable to type 'number'.`);
expect(diags[0].messageText).toBe(`Type 'boolean' is not assignable to type 'number'.`);
expect(getSourceCodeForDiagnostic(diags[0])).toEqual('[fromAbstract]="true"');
expect(diags[1].messageText).toBe(`Type '3' is not assignable to type 'string'.`);
expect(diags[1].messageText).toBe(`Type 'number' is not assignable to type 'string'.`);
expect(getSourceCodeForDiagnostic(diags[1])).toEqual('[fromBase]="3"');
expect(diags[2].messageText).toBe(`Type '4' is not assignable to type 'boolean'.`);
expect(diags[2].messageText).toBe(`Type 'number' is not assignable to type 'boolean'.`);
expect(getSourceCodeForDiagnostic(diags[2])).toEqual('[fromChild]="4"');
});

View File

@ -584,7 +584,7 @@ describe('downlevel decorator transform', () => {
const visitNode = (node: ts.Node): ts.Node => {
if (ts.isClassDeclaration(node) || ts.isClassElement(node)) {
const cloned = ts.getMutableClone(node);
cloned.decorators = undefined;
(cloned.decorators as undefined) = undefined;
return cloned;
}
return ts.visitEachChild(node, visitNode, context);

View File

@ -492,11 +492,11 @@ describe('ng program', () => {
.toBe(true);
switch (checks.shouldBe) {
case ShouldBe.Empty:
expect(writeData!.data).toMatch(/^(\s*\/\*([^*]|\*[^/])*\*\/\s*)?$/);
expect(writeData!.data).toMatch(/^(\s*\/\*([^*]|\*[^\/])*\*\/\s*)?$/);
break;
case ShouldBe.EmptyExport:
expect(writeData!.data)
.toMatch(/^((\s*\/\*([^*]|\*[^/])*\*\/\s*)|(\s*export\s*{\s*}\s*;\s*)|())$/);
.toMatch(/^((\s*\/\*([^*]|\*[^\/])*\*\/\s*)|(\s*export\s*{\s*};\s*))$/m);
break;
case ShouldBe.NoneEmpty:
expect(writeData!.data).not.toBe('');
@ -505,12 +505,14 @@ describe('ng program', () => {
}
assertGenFile(
'built/src/util.ngfactory.js', {originalFileName: 'src/util.ts', shouldBe: ShouldBe.Empty});
'built/src/util.ngfactory.js',
{originalFileName: 'src/util.ts', shouldBe: ShouldBe.EmptyExport});
assertGenFile(
'built/src/util.ngfactory.d.ts',
{originalFileName: 'src/util.ts', shouldBe: ShouldBe.EmptyExport});
assertGenFile(
'built/src/util.ngsummary.js', {originalFileName: 'src/util.ts', shouldBe: ShouldBe.Empty});
'built/src/util.ngsummary.js',
{originalFileName: 'src/util.ts', shouldBe: ShouldBe.EmptyExport});
assertGenFile(
'built/src/util.ngsummary.d.ts',
{originalFileName: 'src/util.ts', shouldBe: ShouldBe.EmptyExport});
@ -987,7 +989,8 @@ describe('ng program', () => {
const errorDiags =
program1.emit().diagnostics.filter(d => d.category === ts.DiagnosticCategory.Error);
expect(stripAnsi(formatDiagnostics(errorDiags)))
.toContain(`src/main.ts:5:13 - error TS2322: Type '1' is not assignable to type 'string'.`);
.toContain(
`src/main.ts:5:13 - error TS2322: Type 'number' is not assignable to type 'string'.`);
expect(stripAnsi(formatDiagnostics(errorDiags)))
.toContain(
`src/main.html:1:1 - error TS100: Property 'nonExistent' does not exist on type 'MyComp'.`);