feat(compiler): enabled strict checking of parameters to an @Injectable
(#19412)
Added the compiler options `strictInjectionParameters` that defaults to `false`. If enabled the compiler will report errors for parameters of an `@Injectable` that cannot be determined instead of generating a warning. This is planned to be switched to default to `true` for Angular 6.0.
This commit is contained in:

committed by
Victor Berchet

parent
a75040d0a1
commit
dfb8d21ef4
@ -52,6 +52,14 @@ export interface CompilerOptions extends ts.CompilerOptions {
|
|||||||
// Don't produce .ngfactory.ts or .ngstyle.ts files
|
// Don't produce .ngfactory.ts or .ngstyle.ts files
|
||||||
skipTemplateCodegen?: boolean;
|
skipTemplateCodegen?: boolean;
|
||||||
|
|
||||||
|
// Always report errors when the type of a parameter supplied whose injection type cannot
|
||||||
|
// be determined. When this value option is not provided or is `false`, constructor
|
||||||
|
// parameters of classes marked with `@Injectable` whose type cannot be resolved will
|
||||||
|
// produce a warning. With this option `true`, they produce an error. When this option is
|
||||||
|
// not provided is treated as if it were `false`. In Angular 6.0, if this option is not
|
||||||
|
// provided, it will be treated as `true`.
|
||||||
|
strictInjectionParameters?: boolean;
|
||||||
|
|
||||||
// Whether to generate a flat module index of the given name and the corresponding
|
// Whether to generate a flat module index of the given name and the corresponding
|
||||||
// flat module metadata. This option is intended to be used when creating flat
|
// flat module metadata. This option is intended to be used when creating flat
|
||||||
// modules similar to how `@angular/core` and `@angular/common` are packaged.
|
// modules similar to how `@angular/core` and `@angular/common` are packaged.
|
||||||
|
@ -69,6 +69,7 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
|
|||||||
enableLegacyTemplate: options.enableLegacyTemplate === true,
|
enableLegacyTemplate: options.enableLegacyTemplate === true,
|
||||||
missingTranslation: options.missingTranslation,
|
missingTranslation: options.missingTranslation,
|
||||||
preserveWhitespaces: options.preserveWhitespaces,
|
preserveWhitespaces: options.preserveWhitespaces,
|
||||||
|
strictInjectionParameters: options.strictInjectionParameters,
|
||||||
});
|
});
|
||||||
const normalizer = new DirectiveNormalizer(
|
const normalizer = new DirectiveNormalizer(
|
||||||
{get: (url: string) => compilerHost.loadResource(url)}, urlResolver, htmlParser, config);
|
{get: (url: string) => compilerHost.loadResource(url)}, urlResolver, htmlParser, config);
|
||||||
|
@ -19,4 +19,5 @@ export interface AotCompilerOptions {
|
|||||||
preserveWhitespaces?: boolean;
|
preserveWhitespaces?: boolean;
|
||||||
fullTemplateTypeCheck?: boolean;
|
fullTemplateTypeCheck?: boolean;
|
||||||
allowEmptyCodegenFiles?: boolean;
|
allowEmptyCodegenFiles?: boolean;
|
||||||
|
strictInjectionParameters?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -21,17 +21,18 @@ export class CompilerConfig {
|
|||||||
public jitDevMode: boolean;
|
public jitDevMode: boolean;
|
||||||
public missingTranslation: MissingTranslationStrategy|null;
|
public missingTranslation: MissingTranslationStrategy|null;
|
||||||
public preserveWhitespaces: boolean;
|
public preserveWhitespaces: boolean;
|
||||||
|
public strictInjectionParameters: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
{defaultEncapsulation = ViewEncapsulation.Emulated, useJit = true, jitDevMode = false,
|
{defaultEncapsulation = ViewEncapsulation.Emulated, useJit = true, jitDevMode = false,
|
||||||
missingTranslation, enableLegacyTemplate, preserveWhitespaces}: {
|
missingTranslation, enableLegacyTemplate, preserveWhitespaces, strictInjectionParameters}: {
|
||||||
defaultEncapsulation?: ViewEncapsulation,
|
defaultEncapsulation?: ViewEncapsulation,
|
||||||
useJit?: boolean,
|
useJit?: boolean,
|
||||||
jitDevMode?: boolean,
|
jitDevMode?: boolean,
|
||||||
missingTranslation?: MissingTranslationStrategy,
|
missingTranslation?: MissingTranslationStrategy,
|
||||||
enableLegacyTemplate?: boolean,
|
enableLegacyTemplate?: boolean,
|
||||||
preserveWhitespaces?: boolean,
|
preserveWhitespaces?: boolean,
|
||||||
fullTemplateTypeCheck?: boolean
|
strictInjectionParameters?: boolean,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
this.defaultEncapsulation = defaultEncapsulation;
|
this.defaultEncapsulation = defaultEncapsulation;
|
||||||
this.useJit = !!useJit;
|
this.useJit = !!useJit;
|
||||||
@ -39,6 +40,7 @@ export class CompilerConfig {
|
|||||||
this.missingTranslation = missingTranslation || null;
|
this.missingTranslation = missingTranslation || null;
|
||||||
this.enableLegacyTemplate = enableLegacyTemplate === true;
|
this.enableLegacyTemplate = enableLegacyTemplate === true;
|
||||||
this.preserveWhitespaces = preserveWhitespacesDefault(noUndefined(preserveWhitespaces));
|
this.preserveWhitespaces = preserveWhitespacesDefault(noUndefined(preserveWhitespaces));
|
||||||
|
this.strictInjectionParameters = strictInjectionParameters === true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -886,10 +886,10 @@ export class CompileMetadataResolver {
|
|||||||
dependenciesMetadata.map((dep) => dep ? stringifyType(dep.token) : '?').join(', ');
|
dependenciesMetadata.map((dep) => dep ? stringifyType(dep.token) : '?').join(', ');
|
||||||
const message =
|
const message =
|
||||||
`Can't resolve all parameters for ${stringifyType(typeOrFunc)}: (${depsTokens}).`;
|
`Can't resolve all parameters for ${stringifyType(typeOrFunc)}: (${depsTokens}).`;
|
||||||
if (throwOnUnknownDeps) {
|
if (throwOnUnknownDeps || this._config.strictInjectionParameters) {
|
||||||
this._reportError(syntaxError(message), typeOrFunc);
|
this._reportError(syntaxError(message), typeOrFunc);
|
||||||
} else {
|
} else {
|
||||||
this._console.warn(`Warning: ${message} This will become an error in Angular v5.x`);
|
this._console.warn(`Warning: ${message} This will become an error in Angular v6.x`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,10 +205,30 @@ describe('compiler (unbundled Angular)', () => {
|
|||||||
const warnSpy = spyOn(console, 'warn');
|
const warnSpy = spyOn(console, 'warn');
|
||||||
compile([FILES, angularFiles]);
|
compile([FILES, angularFiles]);
|
||||||
expect(warnSpy).toHaveBeenCalledWith(
|
expect(warnSpy).toHaveBeenCalledWith(
|
||||||
`Warning: Can't resolve all parameters for MyService in /app/app.ts: (?). This will become an error in Angular v5.x`);
|
`Warning: Can't resolve all parameters for MyService in /app/app.ts: (?). This will become an error in Angular v6.x`);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should error if not all arguments of an @Injectable class can be resolved if strictInjectionParamters is true',
|
||||||
|
() => {
|
||||||
|
const FILES: MockDirectory = {
|
||||||
|
app: {
|
||||||
|
'app.ts': `
|
||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MyService {
|
||||||
|
constructor(a: boolean) {}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const warnSpy = spyOn(console, 'warn');
|
||||||
|
expect(() => compile([FILES, angularFiles], {strictInjectionParameters: true}))
|
||||||
|
.toThrowError(`Can't resolve all parameters for MyService in /app/app.ts: (?).`);
|
||||||
|
expect(warnSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('should be able to suppress a null access', () => {
|
it('should be able to suppress a null access', () => {
|
||||||
const FILES: MockDirectory = {
|
const FILES: MockDirectory = {
|
||||||
app: {
|
app: {
|
||||||
|
Reference in New Issue
Block a user