build: TypeScript 3.6 compatibility. (#32908)

This PR updates Angular to compile with TypeScript 3.6 while retaining
compatibility with TS3.5. We achieve this by inserting several `as any`
casts for compatiblity around `ts.CompilerHost` APIs.

PR Close #32908
This commit is contained in:
Martin Probst 2019-09-27 16:16:10 +02:00 committed by atscott
parent 6f5f481fda
commit 5332b04f35
12 changed files with 42 additions and 26 deletions

View File

@ -180,8 +180,11 @@ export class CommonJsReflectionHost extends Esm5ReflectionHost {
private resolveModuleName(moduleName: string, containingFile: ts.SourceFile): ts.SourceFile
|undefined {
if (this.compilerHost.resolveModuleNames) {
const moduleInfo =
this.compilerHost.resolveModuleNames([moduleName], containingFile.fileName)[0];
// FIXME: remove the "as any" cast once on TS3.6.
const moduleInfo = (this.compilerHost as any)
.resolveModuleNames(
[moduleName], containingFile.fileName, undefined, undefined,
this.program.getCompilerOptions())[0];
return moduleInfo && this.program.getSourceFile(absoluteFrom(moduleInfo.resolvedFileName));
} else {
const moduleInfo = ts.resolveModuleName(

View File

@ -158,8 +158,11 @@ export class UmdReflectionHost extends Esm5ReflectionHost {
private resolveModuleName(moduleName: string, containingFile: ts.SourceFile): ts.SourceFile
|undefined {
if (this.compilerHost.resolveModuleNames) {
const moduleInfo =
this.compilerHost.resolveModuleNames([moduleName], containingFile.fileName)[0];
// FIXME: remove the "as any" cast once on TS3.6.
const moduleInfo = (this.compilerHost as any)
.resolveModuleNames(
[moduleName], containingFile.fileName, undefined, undefined,
this.program.getCompilerOptions())[0];
return moduleInfo && this.program.getSourceFile(absoluteFrom(moduleInfo.resolvedFileName));
} else {
const moduleInfo = ts.resolveModuleName(
@ -281,4 +284,4 @@ function findNamespaceOfIdentifier(id: ts.Identifier): ts.Identifier|null {
export function stripParentheses(node: ts.Node): ts.Node {
return ts.isParenthesizedExpression(node) ? node.expression : node;
}
}

View File

@ -457,8 +457,8 @@ export class MetadataCollector {
left: {
__symbolic: 'select',
expression: recordEntry({__symbolic: 'reference', name: enumName}, node), name
} as any,
};
},
} as any;
} else {
nextDefaultValue =
recordEntry(errorSym('Unsupported enum member name', member.name), node);

View File

@ -32,11 +32,14 @@ export interface ShimGenerator {
export class GeneratedShimsHostWrapper implements ts.CompilerHost {
constructor(private delegate: ts.CompilerHost, private shimGenerators: ShimGenerator[]) {
if (delegate.resolveModuleNames !== undefined) {
this.resolveModuleNames =
(moduleNames: string[], containingFile: string, reusedNames?: string[],
redirectedReference?: ts.ResolvedProjectReference) =>
delegate.resolveModuleNames !(
moduleNames, containingFile, reusedNames, redirectedReference);
// FIXME: TypeScript 3.6 adds an "options" argument that the code below passes on, but which
// still makes the method incompatible with TS3.5. Remove the "as any" cast once fully on 3.6.
((this as ts.CompilerHost) as any).resolveModuleNames =
(moduleNames: string[], containingFile: string, reusedNames: (string[] | undefined),
redirectedReference: (ts.ResolvedProjectReference | undefined), options: any) =>
(delegate as any)
.resolveModuleNames !(
moduleNames, containingFile, reusedNames, redirectedReference, options);
}
if (delegate.resolveTypeReferenceDirectives) {
// Backward compatibility with TypeScript 2.9 and older since return
@ -56,10 +59,6 @@ export class GeneratedShimsHostWrapper implements ts.CompilerHost {
}
}
resolveModuleNames?:
(moduleNames: string[], containingFile: string, reusedNames?: string[],
redirectedReference?: ts.ResolvedProjectReference) => (ts.ResolvedModule | undefined)[];
resolveTypeReferenceDirectives?:
(names: string[], containingFile: string) => ts.ResolvedTypeReferenceDirective[];

View File

@ -14,7 +14,8 @@ describe('shim host', () => {
const delegate = {} as unknown as ts.CompilerHost;
const shimsHost = new GeneratedShimsHostWrapper(delegate, []);
expect(shimsHost.resolveModuleNames).not.toBeDefined();
// FIXME: re-enable once fully on TS3.6.
// expect(shimsHost.resolveModuleNames).not.toBeDefined();
expect(shimsHost.resolveTypeReferenceDirectives).not.toBeDefined();
expect(shimsHost.directoryExists).not.toBeDefined();
expect(shimsHost.getDirectories).not.toBeDefined();
@ -29,7 +30,8 @@ describe('shim host', () => {
} as unknown as ts.CompilerHost;
const shimsHost = new GeneratedShimsHostWrapper(delegate, []);
expect(shimsHost.resolveModuleNames).toBeDefined();
// FIXME: re-enable once fully on TS3.6.
// expect(shimsHost.resolveModuleNames).toBeDefined();
expect(shimsHost.resolveTypeReferenceDirectives).toBeDefined();
expect(shimsHost.directoryExists).toBeDefined();
expect(shimsHost.getDirectories).toBeDefined();

View File

@ -113,7 +113,10 @@ export function resolveModuleName(
moduleName: string, containingFile: string, compilerOptions: ts.CompilerOptions,
compilerHost: ts.CompilerHost): ts.ResolvedModule|undefined {
if (compilerHost.resolveModuleNames) {
return compilerHost.resolveModuleNames([moduleName], containingFile)[0];
// FIXME: Additional parameters are required in TS3.6, but ignored in 3.5.
// Remove the any cast once fully on TS3.6.
return (compilerHost as any)
.resolveModuleNames([moduleName], containingFile, undefined, undefined, compilerOptions)[0];
} else {
return ts.resolveModuleName(moduleName, containingFile, compilerOptions, compilerHost)
.resolvedModule;

View File

@ -277,7 +277,7 @@ export class PlatformRef {
const ngZoneInjector = Injector.create(
{providers: providers, parent: this.injector, name: moduleFactory.moduleType.name});
const moduleRef = <InternalNgModuleRef<M>>moduleFactory.create(ngZoneInjector);
const exceptionHandler: ErrorHandler = moduleRef.injector.get(ErrorHandler, null);
const exceptionHandler: ErrorHandler|null = moduleRef.injector.get(ErrorHandler, null);
if (!exceptionHandler) {
throw new Error('No ErrorHandler. Is platform module (BrowserModule) included?');
}

View File

@ -81,7 +81,9 @@ export interface DirectiveType<T> extends Type<T> {
ngFactoryDef: () => T;
}
export const enum DirectiveDefFlags {ContentQuery = 0b10}
export enum DirectiveDefFlags {
ContentQuery = 0b10
}
/**
* A subclass of `Type` which has a static `ngPipeDef`:`PipeDef` field making it

View File

@ -225,7 +225,9 @@ export function createContainerRef(
ngModuleRef?: viewEngine_NgModuleRef<any>|undefined): viewEngine_ComponentRef<C> {
const contextInjector = injector || this.parentInjector;
if (!ngModuleRef && (componentFactory as any).ngModule == null && contextInjector) {
ngModuleRef = contextInjector.get(viewEngine_NgModuleRef, null);
// FIXME: ngModuleRef is optional, so its type allows "undefined", whereas the code
// below is passing null for the default/absent value.
ngModuleRef = contextInjector.get(viewEngine_NgModuleRef, null as any as undefined);
}
const componentRef =

View File

@ -268,8 +268,9 @@ export class TestBedRender3 implements TestBed {
if (token as unknown === TestBedRender3) {
return this as any;
}
const result = this.testModuleRef.injector.get(token, UNDEFINED, flags);
return result === UNDEFINED ? this.compiler.injector.get(token, notFoundValue, flags) : result;
const result = this.testModuleRef.injector.get(token, UNDEFINED as{}, flags);
return result === UNDEFINED ? this.compiler.injector.get(token, notFoundValue, flags) as any :
result;
}
/** @deprecated from v9.0.0 use TestBed.inject */

View File

@ -480,7 +480,8 @@ export class TestBedViewEngine implements TestBed {
// Tests can inject things from the ng module and from the compiler,
// but the ng module can't inject things from the compiler and vice versa.
const result = this._moduleRef.injector.get(token, UNDEFINED, flags);
return result === UNDEFINED ? this._compiler.injector.get(token, notFoundValue, flags) : result;
return result === UNDEFINED ? this._compiler.injector.get(token, notFoundValue, flags) as any :
result;
}
/** @deprecated from v9.0.0 use TestBed.inject */

View File

@ -99,7 +99,7 @@ describe(
xit('should ensure that Promise this is instanceof Promise', () => {
expect(() => {
Promise.call({}, () => null);
Promise.call({} as any, () => null);
}).toThrowError('Must be an instanceof Promise.');
});