build: support building with TypeScript 4.1 (#39571)

TypeScript 4.1 is now used to build and test within the repository.

PR Close #39571
This commit is contained in:
Charles Lyding 2020-11-04 19:58:29 -05:00 committed by Jessica Janiuk
parent a7e7c211b5
commit 318255a5f8
32 changed files with 93 additions and 69 deletions

View File

@ -49,6 +49,6 @@
"supertest": "^4.0.2",
"tslint": "^6.1.3",
"tslint-jasmine-noSkipOrFocus": "^1.0.9",
"typescript": "^4.0.2"
"typescript": "^4.1.2"
}
}

View File

@ -2563,10 +2563,10 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
typescript@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2"
integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==
typescript@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.2.tgz#6369ef22516fe5e10304aae5a5c4862db55380e9"
integrity sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==
undefsafe@^2.0.2:
version "2.0.2"

View File

@ -304,6 +304,6 @@ class FakeModuleFactory extends NgModuleFactory<any> {
function returnPromisesFromSpy(spy: jasmine.Spy): Deferred[] {
const deferreds: Deferred[] = [];
spy.and.callFake(() => new Promise((resolve, reject) => deferreds.push({resolve, reject})));
spy.and.callFake(() => new Promise<void>((resolve, reject) => deferreds.push({resolve, reject})));
return deferreds;
}

View File

@ -13658,7 +13658,7 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@4.0.5:
typescript@4.0.5, typescript@~4.0.2:
version "4.0.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.5.tgz#ae9dddfd1069f1cb5beb3ef3b2170dd7c1332389"
integrity sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==
@ -13668,11 +13668,6 @@ typescript@^3.2.2:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==
typescript@~4.0.2:
version "4.0.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.3.tgz#153bbd468ef07725c1df9c77e8b453f8d36abba5"
integrity sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==
ua-parser-js@0.7.21:
version "0.7.21"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.21.tgz#853cf9ce93f642f67174273cc34565ae6f308777"

View File

@ -18,8 +18,8 @@ export abstract class BaseModule<Data> {
protected git: GitClient, protected config: NgDevConfig<{caretaker: CaretakerConfig}>) {}
/** Asyncronously retrieve data for the module. */
protected abstract async retrieveData(): Promise<Data>;
protected abstract retrieveData(): Promise<Data>;
/** Print the information discovered for the module to the terminal. */
abstract async printToTerminal(): Promise<void>;
abstract printToTerminal(): Promise<void>;
}

View File

@ -83,7 +83,7 @@ export async function execTimed(description: string, func: () => Promise<void>)
}
export async function nextTick(delay = 1) {
return new Promise((res, rej) => {
return new Promise<void>((res, rej) => {
setTimeout(() => {
res();
}, delay);

View File

@ -150,7 +150,7 @@
"tsickle": "0.38.1",
"tslib": "^2.0.0",
"tslint": "6.1.3",
"typescript": "~4.0.2",
"typescript": "~4.1.2",
"xhr2": "0.2.0",
"yaml": "^1.10.0",
"yargs": "^16.1.1"

View File

@ -810,7 +810,7 @@ export class TransitionAnimationEngine {
}
whenRenderingDone(): Promise<any> {
return new Promise(resolve => {
return new Promise<void>(resolve => {
if (this.players.length) {
return optimizeGroupPlayer(this.players).onDone(() => resolve());
} else {

View File

@ -28,7 +28,7 @@
"@angular/compiler-cli": "0.0.0-PLACEHOLDER",
"@bazel/typescript": ">=1.0.0",
"terser": "^4.3.1",
"typescript": ">=4.0 <4.1",
"typescript": ">=4.0 <4.2",
"rollup": ">=1.20.0",
"rollup-plugin-commonjs": ">=9.0.0",
"rollup-plugin-node-resolve": ">=4.2.0",

View File

@ -43,7 +43,7 @@ export class Options {
}
function writeFile(filename: string, content: string): Promise<any> {
return new Promise(function(resolve, reject) {
return new Promise<void>(function(resolve, reject) {
fs.writeFile(filename, content, (error) => {
if (error) {
reject(error);

View File

@ -78,7 +78,7 @@ export function findNamespaceOfIdentifier(id: ts.Identifier): ts.Identifier|null
export function findRequireCallReference(id: ts.Identifier, checker: ts.TypeChecker): RequireCall|
null {
const symbol = checker.getSymbolAtLocation(id) || null;
const declaration = symbol && symbol.valueDeclaration;
const declaration = symbol?.valueDeclaration ?? symbol?.declarations?.[0];
const initializer =
declaration && ts.isVariableDeclaration(declaration) && declaration.initializer || null;
return initializer && isRequireCall(initializer) ? initializer : null;

View File

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

View File

@ -126,9 +126,9 @@ enum TsStructureIsReused {
Completely = 2,
}
export function expectCompleteReuse(oldProgram: ts.Program): void {
export function expectCompleteReuse(program: ts.Program): void {
// Assert complete reuse using TypeScript's private API.
expect((oldProgram as any).structureIsReused)
expect((program as any).structureIsReused)
.toBe(TsStructureIsReused.Completely, COMPLETE_REUSE_FAILURE_MESSAGE);
}

View File

@ -44,7 +44,7 @@ runInEachFileSystem(() => {
programStrategy.updateFiles(
new Map([[typecheckPath, 'export const VERSION = 2;']]), UpdateMode.Complete);
expectCompleteReuse(program);
expectCompleteReuse(programStrategy.getProgram());
});
it('should have complete reuse if no structural changes are made to input files', () => {
@ -56,7 +56,7 @@ runInEachFileSystem(() => {
programStrategy.updateFiles(
new Map([[mainPath, 'export const STILL_NOT_A_COMPONENT = true;']]), UpdateMode.Complete);
expectCompleteReuse(program);
expectCompleteReuse(programStrategy.getProgram());
});
});
});

View File

@ -127,7 +127,7 @@ export function performWatchCompilation(host: PerformWatchHost):
// Watch basePath, ignoring .dotfiles
let resolveReadyPromise: () => void;
const readyPromise = new Promise(resolve => resolveReadyPromise = resolve);
const readyPromise = new Promise<void>(resolve => resolveReadyPromise = resolve);
// Note: ! is ok as options are filled after the first compilation
// Note: ! is ok as resolvedReadyPromise is filled by the previous call
const fileWatcher =

View File

@ -48,6 +48,17 @@ const LOWER_FIELDS = ['useValue', 'useFactory', 'data', 'id', 'loadChildren'];
*/
const R3_LOWER_FIELDS = [...LOWER_FIELDS, 'providers', 'imports', 'exports'];
/**
* Installs a handler for testing purposes to allow inspection of the temporary program.
*/
let tempProgramHandlerForTest: ((program: ts.Program) => void)|null = null;
export function setTempProgramHandlerForTest(handler: (program: ts.Program) => void): void {
tempProgramHandlerForTest = handler;
}
export function resetTempProgramHandlerForTest(): void {
tempProgramHandlerForTest = null;
}
const emptyModules: NgAnalyzedModules = {
ngModules: [],
ngModuleByPipeOrDirective: new Map(),
@ -618,6 +629,9 @@ class AngularCompilerProgram implements Program {
}
const tmpProgram = ts.createProgram(rootNames, this.options, this.hostAdapter, oldTsProgram);
if (tempProgramHandlerForTest !== null) {
tempProgramHandlerForTest(tmpProgram);
}
const sourceFiles: string[] = [];
const tsFiles: string[] = [];
tmpProgram.getSourceFiles().forEach(sf => {

View File

@ -1671,7 +1671,7 @@ describe('ngc transformer command-line', () => {
const config = readCommandLineAndConfiguration(['-p', basePath]);
const compile = watchMode(config.project, config.options, errorSpy);
return new Promise(resolve => {
return new Promise<void>(resolve => {
compile.ready(() => {
cb();

View File

@ -358,7 +358,7 @@ export declare class AnimationEvent {
@Component({
selector: 'test',
template: '<div dir [foo]="invalid && 1"></div>',
template: '<div dir [foo]="!!invalid"></div>',
})
class TestCmp {}
@ -379,7 +379,7 @@ export declare class AnimationEvent {
const diags = env.driveDiagnostics();
expect(diags.length).toBe(2);
expect(diags[0].messageText).toEqual(`Type 'number' is not assignable to type 'string'.`);
expect(diags[0].messageText).toEqual(`Type 'boolean' is not assignable to type 'string'.`);
expect(diags[1].messageText)
.toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
});
@ -389,7 +389,7 @@ export declare class AnimationEvent {
const diags = env.driveDiagnostics();
expect(diags.length).toBe(2);
expect(diags[0].messageText).toEqual(`Type 'number' is not assignable to type 'string'.`);
expect(diags[0].messageText).toEqual(`Type 'boolean' is not assignable to type 'string'.`);
expect(diags[1].messageText)
.toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
});
@ -411,15 +411,15 @@ export declare class AnimationEvent {
@Component({
selector: 'test',
template: '<div dir [foo]="invalid && nullable"></div>',
template: '<div dir [foo]="!!invalid && nullable"></div>',
})
class TestCmp {
nullable: string | null | undefined;
nullable: boolean | null | undefined;
}
@Directive({selector: '[dir]'})
class TestDir {
@Input() foo: string;
@Input() foo: boolean;
}
@NgModule({
@ -436,7 +436,7 @@ export declare class AnimationEvent {
const diags = env.driveDiagnostics();
expect(diags.length).toBe(2);
expect((diags[0].messageText as ts.DiagnosticMessageChain).messageText)
.toEqual(`Type 'string | null | undefined' is not assignable to type 'string'.`);
.toEqual(`Type 'boolean | null | undefined' is not assignable to type 'boolean'.`);
expect(diags[1].messageText)
.toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
});
@ -448,7 +448,7 @@ export declare class AnimationEvent {
const diags = env.driveDiagnostics();
expect(diags.length).toBe(2);
expect((diags[0].messageText as ts.DiagnosticMessageChain).messageText)
.toEqual(`Type 'string | null | undefined' is not assignable to type 'string'.`);
.toEqual(`Type 'boolean | null | undefined' is not assignable to type 'boolean'.`);
expect(diags[1].messageText)
.toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
});
@ -470,15 +470,15 @@ export declare class AnimationEvent {
@Component({
selector: 'test',
template: '<div dir [foo]="invalid && user?.name"></div>',
template: '<div dir [foo]="!!invalid && user?.isMember"></div>',
})
class TestCmp {
user?: {name: string};
user?: {isMember: boolean};
}
@Directive({selector: '[dir]'})
class TestDir {
@Input() foo: string;
@Input() foo: boolean;
}
@NgModule({
@ -499,7 +499,7 @@ export declare class AnimationEvent {
const diags = env.driveDiagnostics();
expect(diags.length).toBe(2);
expect((diags[0].messageText as ts.DiagnosticMessageChain).messageText)
.toEqual(`Type 'string | undefined' is not assignable to type 'string'.`);
.toEqual(`Type 'boolean | undefined' is not assignable to type 'boolean'.`);
expect(diags[1].messageText)
.toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
});
@ -511,7 +511,7 @@ export declare class AnimationEvent {
const diags = env.driveDiagnostics();
expect(diags.length).toBe(2);
expect((diags[0].messageText as ts.DiagnosticMessageChain).messageText)
.toEqual(`Type 'string | undefined' is not assignable to type 'string'.`);
.toEqual(`Type 'boolean | undefined' is not assignable to type 'boolean'.`);
expect(diags[1].messageText)
.toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
});
@ -2325,7 +2325,8 @@ export declare class AnimationEvent {
env.write('other.ts', `export const VERSION = 2;`);
env.driveMain();
expectCompleteReuse(firstProgram);
expectCompleteReuse(env.getTsProgram());
expectCompleteReuse(env.getReuseTsProgram());
});
});
});

View File

@ -13,7 +13,7 @@ import * as ts from 'typescript';
import {formatDiagnostics} from '../../src/perform_compile';
import {CompilerHost, EmitFlags, LazyRoute} from '../../src/transformers/api';
import {createSrcToOutPathMapper} from '../../src/transformers/program';
import {createSrcToOutPathMapper, resetTempProgramHandlerForTest, setTempProgramHandlerForTest} from '../../src/transformers/program';
import {StructureIsReused, tsStructureIsReused} from '../../src/transformers/util';
import {expectNoDiagnosticsInProgram, setup, stripAnsi, TestSupport} from '../test_support';
@ -297,6 +297,17 @@ describe('ng program', () => {
describe(
'verify that program structure is reused within tsc in order to speed up incremental compilation',
() => {
afterEach(resetTempProgramHandlerForTest);
function captureStructureReuse(compile: () => void): StructureIsReused|null {
let structureReuse: StructureIsReused|null = null;
setTempProgramHandlerForTest(program => {
structureReuse = tsStructureIsReused(program);
});
compile();
return structureReuse;
}
it('should reuse the old ts program completely if nothing changed', () => {
testSupport.writeFiles({'src/index.ts': createModuleAndCompSource('main')});
const host = createWatchModeHost();
@ -304,8 +315,9 @@ describe('ng program', () => {
// and therefore changes the structure again
const p1 = compile(undefined, undefined, undefined, host).program;
const p2 = compile(p1, undefined, undefined, host).program;
compile(p2, undefined, undefined, host);
expect(tsStructureIsReused(p2.getTsProgram())).toBe(StructureIsReused.Completely);
const structureReuse =
captureStructureReuse(() => compile(p2, undefined, undefined, host));
expect(structureReuse).toBe(StructureIsReused.Completely);
});
it('should reuse the old ts program completely if a template or a ts file changed',
@ -328,8 +340,9 @@ describe('ng program', () => {
'src/main.html': `Another template`,
'src/util.ts': `export const x = 2`,
});
compile(p2, undefined, undefined, host);
expect(tsStructureIsReused(p2.getTsProgram())).toBe(StructureIsReused.Completely);
const structureReuse =
captureStructureReuse(() => compile(p2, undefined, undefined, host));
expect(structureReuse).toBe(StructureIsReused.Completely);
});
it('should not reuse the old ts program if an import changed', () => {
@ -348,8 +361,9 @@ describe('ng program', () => {
const p2 = compile(p1, undefined, undefined, host).program;
testSupport.writeFiles(
{'src/util.ts': `import {Injectable} from '@angular/core'; export const x = 1;`});
compile(p2, undefined, undefined, host);
expect(tsStructureIsReused(p2.getTsProgram())).toBe(StructureIsReused.SafeModules);
const structureReuse =
captureStructureReuse(() => compile(p2, undefined, undefined, host));
expect(structureReuse).toBe(StructureIsReused.SafeModules);
});
});
});

View File

@ -1048,7 +1048,7 @@ function expectParseTemplateBindingsError(attribute: string, error: string) {
function _parseTemplateBindings(attribute: string, templateUrl: string) {
const match = attribute.match(/^\*(.+)="(.*)"$/);
expect(match).toBeTruthy(`failed to extract key and value from ${attribute}`);
const [_, key, value] = match;
const [_, key, value] = match!;
const absKeyOffset = 1; // skip the * prefix
const absValueOffset = attribute.indexOf('=') + '="'.length;
const parser = createParser();

View File

@ -53,7 +53,7 @@ function getMetaRenderHook(doc: any) {
function getAsyncTitleRenderHook(doc: any) {
return () => {
// Async set the title as part of the render hook.
return new Promise(resolve => {
return new Promise<void>(resolve => {
setTimeout(() => {
doc.title = 'AsyncRenderHook';
resolve();
@ -64,7 +64,7 @@ function getAsyncTitleRenderHook(doc: any) {
function asyncRejectRenderHook() {
return () => {
return new Promise((_resolve, reject) => {
return new Promise<void>((_resolve, reject) => {
setTimeout(() => {
reject('reject');
});

View File

@ -97,7 +97,7 @@ export function validateInjectionKey(
export class Deferred<R> {
promise: Promise<R>;
// TODO(issue/24571): remove '!'.
resolve!: (value?: R|PromiseLike<R>) => void;
resolve!: (value: R|PromiseLike<R>) => void;
// TODO(issue/24571): remove '!'.
reject!: (error?: any) => void;

View File

@ -396,7 +396,7 @@ export class UpgradeAdapter {
this.ngZone.run(() => {
bootstrap(element, [this.ng1Module.name], config!);
});
const ng1BootstrapPromise = new Promise((resolve) => {
const ng1BootstrapPromise = new Promise<void>((resolve) => {
if (windowAngular.resumeBootstrap) {
const originalResumeBootstrap: () => void = windowAngular.resumeBootstrap;
windowAngular.resumeBootstrap = function() {

View File

@ -19,7 +19,7 @@
"mocha": "^3.1.2",
"mock-require": "3.0.3",
"promises-aplus-tests": "^2.1.2",
"typescript": "4.0.2"
"typescript": "4.1.2"
},
"scripts": {
"closuretest": "./scripts/closure/closure_compiler.sh",

View File

@ -65,7 +65,7 @@ ifEnvSupports(supportJasmineSpec, () => {
});
it('should wait for promise to resolve', () => {
return new Promise((res, _) => {
return new Promise<void>((res, _) => {
setTimeout(() => {
log.push('resolved');
res();

View File

@ -103,7 +103,7 @@ ifEnvSupports('Mocha', function() {
});
it('should wait for promise to resolve', () => {
return new Promise((res, _) => {
return new Promise<void>((res, _) => {
setTimeout(() => {
log.push('resolved');
res();

View File

@ -14,6 +14,6 @@
"zone.js": "file:../../../../dist/bin/packages/zone.js/npm_package"
},
"devDependencies": {
"typescript": "~4.0.2"
"typescript": "~4.1.2"
}
}

View File

@ -98,7 +98,7 @@ describe('AsyncTestZoneSpec', function() {
const atz = Zone.current.fork(testZoneSpec);
atz.run(function() {
new Promise((resolve) => {
new Promise<void>((resolve) => {
setTimeout(() => {
resolve();
}, 10);

View File

@ -942,7 +942,7 @@ describe('FakeAsyncTestZoneSpec', () => {
});
it('should wait for promise to resolve', () => {
return new Promise((res, _) => {
return new Promise<void>((res, _) => {
setTimeout(() => {
log.push('resolved');
res();

View File

@ -3786,10 +3786,10 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
typescript@4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2"
integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==
typescript@4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.2.tgz#6369ef22516fe5e10304aae5a5c4862db55380e9"
integrity sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==
underscore@~1.8.3:
version "1.8.3"

View File

@ -33,7 +33,7 @@
"chai": "^4.1.2",
"jasmine": "^3.1.0",
"source-map-support": "^0.5.9",
"typescript": "4.0.2"
"typescript": "4.1.2"
},
"repository": {},
"keywords": [
@ -51,4 +51,4 @@
"url": "https://github.com/angular/angular/issues"
},
"homepage": "https://github.com/angular/angular/tools/ts-api-guardian"
}
}

View File

@ -15621,10 +15621,10 @@ typescript@~3.7.2:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==
typescript@~4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2"
integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==
typescript@~4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.2.tgz#6369ef22516fe5e10304aae5a5c4862db55380e9"
integrity sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==
uglify-js@^1.3.3:
version "1.3.5"