feat(core): Create StaticInjector which does not depend on Reflect polyfill.
This commit is contained in:

committed by
Victor Berchet

parent
f69561b2de
commit
d9d00bd9b5
@ -18,7 +18,7 @@ export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref';
|
||||
|
||||
export {Injector} from './di/injector';
|
||||
export {ReflectiveInjector} from './di/reflective_injector';
|
||||
export {Provider, TypeProvider, ValueProvider, ClassProvider, ExistingProvider, FactoryProvider} from './di/provider';
|
||||
export {StaticProvider, ValueProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ClassProvider} from './di/provider';
|
||||
export {ResolvedReflectiveFactory, ResolvedReflectiveProvider} from './di/reflective_provider';
|
||||
export {ReflectiveKey} from './di/reflective_key';
|
||||
export {InjectionToken, OpaqueToken} from './di/injection_token';
|
||||
|
@ -9,7 +9,10 @@
|
||||
import {Type} from '../type';
|
||||
import {stringify} from '../util';
|
||||
|
||||
import {resolveForwardRef} from './forward_ref';
|
||||
import {InjectionToken} from './injection_token';
|
||||
import {Inject, Optional, Self, SkipSelf} from './metadata';
|
||||
import {ConstructorProvider, ExistingProvider, FactoryProvider, StaticClassProvider, StaticProvider, ValueProvider} from './provider';
|
||||
|
||||
const _THROW_IF_NOT_FOUND = new Object();
|
||||
export const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
|
||||
@ -17,7 +20,7 @@ export const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
|
||||
class _NullInjector implements Injector {
|
||||
get(token: any, notFoundValue: any = _THROW_IF_NOT_FOUND): any {
|
||||
if (notFoundValue === _THROW_IF_NOT_FOUND) {
|
||||
throw new Error(`No provider for ${stringify(token)}!`);
|
||||
throw new Error(`NullInjectorError: No provider for ${stringify(token)}!`);
|
||||
}
|
||||
return notFoundValue;
|
||||
}
|
||||
@ -60,4 +63,307 @@ export abstract class Injector {
|
||||
* @suppress {duplicate}
|
||||
*/
|
||||
abstract get(token: any, notFoundValue?: any): any;
|
||||
|
||||
/**
|
||||
* Create a new Injector which is configure using `StaticProvider`s.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/provider_spec.ts region='ConstructorProvider'}
|
||||
*/
|
||||
static create(providers: StaticProvider[], parent?: Injector): Injector {
|
||||
return new StaticInjector(providers, parent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const IDENT = function<T>(value: T): T {
|
||||
return value;
|
||||
};
|
||||
const EMPTY = <any[]>[];
|
||||
const CIRCULAR = IDENT;
|
||||
const MULTI_PROVIDER_FN = function(): any[] {
|
||||
return Array.prototype.slice.call(arguments);
|
||||
};
|
||||
const GET_PROPERTY_NAME = {} as any;
|
||||
const USE_VALUE =
|
||||
getClosureSafeProperty<ValueProvider>({provide: String, useValue: GET_PROPERTY_NAME});
|
||||
const NG_TOKEN_PATH = 'ngTokenPath';
|
||||
const NG_TEMP_TOKEN_PATH = 'ngTempTokenPath';
|
||||
const enum OptionFlags {
|
||||
Optional = 1 << 0,
|
||||
CheckSelf = 1 << 1,
|
||||
CheckParent = 1 << 2,
|
||||
Default = CheckSelf | CheckParent
|
||||
}
|
||||
const NULL_INJECTOR = Injector.NULL;
|
||||
const NEW_LINE = /\n/gm;
|
||||
const NO_NEW_LINE = 'ɵ';
|
||||
|
||||
export class StaticInjector implements Injector {
|
||||
readonly parent: Injector;
|
||||
|
||||
private _records: Map<any, Record>;
|
||||
|
||||
constructor(providers: StaticProvider[], parent: Injector = NULL_INJECTOR) {
|
||||
this.parent = parent;
|
||||
const records = this._records = new Map<any, Record>();
|
||||
records.set(
|
||||
Injector, <Record>{token: Injector, fn: IDENT, deps: EMPTY, value: this, useNew: false});
|
||||
recursivelyProcessProviders(records, providers);
|
||||
}
|
||||
|
||||
get<T>(token: Type<T>|InjectionToken<T>, notFoundValue?: T): T;
|
||||
get(token: any, notFoundValue?: any): any;
|
||||
get(token: any, notFoundValue?: any): any {
|
||||
const record = this._records.get(token);
|
||||
try {
|
||||
return tryResolveToken(token, record, this._records, this.parent, notFoundValue);
|
||||
} catch (e) {
|
||||
const tokenPath: any[] = e[NG_TEMP_TOKEN_PATH];
|
||||
e.message = formatError('\n' + e.message, tokenPath);
|
||||
e[NG_TOKEN_PATH] = tokenPath;
|
||||
e[NG_TEMP_TOKEN_PATH] = null;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
const tokens = <string[]>[], records = this._records;
|
||||
records.forEach((v, token) => tokens.push(stringify(token)));
|
||||
return `StaticInjector[${tokens.join(', ')}]`;
|
||||
}
|
||||
}
|
||||
|
||||
type SupportedProvider =
|
||||
ValueProvider | ExistingProvider | StaticClassProvider | ConstructorProvider | FactoryProvider;
|
||||
|
||||
interface Record {
|
||||
fn: Function;
|
||||
useNew: boolean;
|
||||
deps: DependencyRecord[];
|
||||
value: any;
|
||||
}
|
||||
|
||||
interface DependencyRecord {
|
||||
token: any;
|
||||
options: number;
|
||||
}
|
||||
|
||||
type TokenPath = Array<any>;
|
||||
|
||||
function resolveProvider(provider: SupportedProvider): Record {
|
||||
const deps = computeDeps(provider);
|
||||
let fn: Function = IDENT;
|
||||
let value: any = EMPTY;
|
||||
let useNew: boolean = false;
|
||||
let provide = resolveForwardRef(provider.provide);
|
||||
if (USE_VALUE in provider) {
|
||||
// We need to use USE_VALUE in provider since provider.useValue could be defined as undefined.
|
||||
value = (provider as ValueProvider).useValue;
|
||||
} else if ((provider as FactoryProvider).useFactory) {
|
||||
fn = (provider as FactoryProvider).useFactory;
|
||||
} else if ((provider as ExistingProvider).useExisting) {
|
||||
// Just use IDENT
|
||||
} else if ((provider as StaticClassProvider).useClass) {
|
||||
useNew = true;
|
||||
fn = resolveForwardRef((provider as StaticClassProvider).useClass);
|
||||
} else if (typeof provide == 'function') {
|
||||
useNew = true;
|
||||
fn = provide;
|
||||
} else {
|
||||
throw staticError(
|
||||
'StaticProvider does not have [useValue|useFactory|useExisting|useClass] or [provide] is not newable',
|
||||
provider);
|
||||
}
|
||||
return {deps, fn, useNew, value};
|
||||
}
|
||||
|
||||
function multiProviderMixError(token: any) {
|
||||
return staticError('Cannot mix multi providers and regular providers', token);
|
||||
}
|
||||
|
||||
function recursivelyProcessProviders(records: Map<any, Record>, provider: StaticProvider) {
|
||||
if (provider) {
|
||||
provider = resolveForwardRef(provider);
|
||||
if (provider instanceof Array) {
|
||||
// if we have an array recurse into the array
|
||||
for (let i = 0; i < provider.length; i++) {
|
||||
recursivelyProcessProviders(records, provider[i]);
|
||||
}
|
||||
} else if (typeof provider === 'function') {
|
||||
// Functions were supported in ReflectiveInjector, but are not here. For safety give useful
|
||||
// error messages
|
||||
throw staticError('Function/Class not supported', provider);
|
||||
} else if (provider && typeof provider === 'object' && provider.provide) {
|
||||
// At this point we have what looks like a provider: {provide: ?, ....}
|
||||
let token = resolveForwardRef(provider.provide);
|
||||
const resolvedProvider = resolveProvider(provider);
|
||||
if (provider.multi === true) {
|
||||
// This is a multi provider.
|
||||
let multiProvider: Record|undefined = records.get(token);
|
||||
if (multiProvider) {
|
||||
if (multiProvider.fn !== MULTI_PROVIDER_FN) {
|
||||
throw multiProviderMixError(token);
|
||||
}
|
||||
} else {
|
||||
// Create a placeholder factory which will look up the constituents of the multi provider.
|
||||
records.set(token, multiProvider = <Record>{
|
||||
token: provider.provide,
|
||||
deps: [],
|
||||
useNew: false,
|
||||
fn: MULTI_PROVIDER_FN,
|
||||
value: EMPTY
|
||||
});
|
||||
}
|
||||
// Treat the provider as the token.
|
||||
token = provider;
|
||||
multiProvider.deps.push({token, options: OptionFlags.Default});
|
||||
}
|
||||
const record = records.get(token);
|
||||
if (record && record.fn == MULTI_PROVIDER_FN) {
|
||||
throw multiProviderMixError(token);
|
||||
}
|
||||
records.set(token, resolvedProvider);
|
||||
} else {
|
||||
throw staticError('Unexpected provider', provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function tryResolveToken(
|
||||
token: any, record: Record | undefined, records: Map<any, Record>, parent: Injector,
|
||||
notFoundValue: any): any {
|
||||
try {
|
||||
return resolveToken(token, record, records, parent, notFoundValue);
|
||||
} catch (e) {
|
||||
// ensure that 'e' is of type Error.
|
||||
if (!(e instanceof Error)) {
|
||||
e = new Error(e);
|
||||
}
|
||||
const path: any[] = e[NG_TEMP_TOKEN_PATH] = e[NG_TEMP_TOKEN_PATH] || [];
|
||||
path.unshift(token);
|
||||
if (record && record.value == CIRCULAR) {
|
||||
// Reset the Circular flag.
|
||||
record.value = EMPTY;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
function resolveToken(
|
||||
token: any, record: Record | undefined, records: Map<any, Record>, parent: Injector,
|
||||
notFoundValue: any): any {
|
||||
let value;
|
||||
if (record) {
|
||||
// If we don't have a record, this implies that we don't own the provider hence don't know how
|
||||
// to resolve it.
|
||||
value = record.value;
|
||||
if (value == CIRCULAR) {
|
||||
throw Error(NO_NEW_LINE + 'Circular dependency');
|
||||
} else if (value === EMPTY) {
|
||||
record.value = CIRCULAR;
|
||||
let obj = undefined;
|
||||
let useNew = record.useNew;
|
||||
let fn = record.fn;
|
||||
let depRecords = record.deps;
|
||||
let deps = EMPTY;
|
||||
if (depRecords.length) {
|
||||
deps = [];
|
||||
for (let i = 0; i < depRecords.length; i++) {
|
||||
const depRecord: DependencyRecord = depRecords[i];
|
||||
const options = depRecord.options;
|
||||
const childRecord =
|
||||
options & OptionFlags.CheckSelf ? records.get(depRecord.token) : undefined;
|
||||
deps.push(tryResolveToken(
|
||||
// Current Token to resolve
|
||||
depRecord.token,
|
||||
// A record which describes how to resolve the token.
|
||||
// If undefined, this means we don't have such a record
|
||||
childRecord,
|
||||
// Other records we know about.
|
||||
records,
|
||||
// If we don't know how to resolve dependency and we should not check parent for it,
|
||||
// than pass in Null injector.
|
||||
!childRecord && !(options & OptionFlags.CheckParent) ? NULL_INJECTOR : parent,
|
||||
options & OptionFlags.Optional ? null : Injector.THROW_IF_NOT_FOUND));
|
||||
}
|
||||
}
|
||||
record.value = value = useNew ? new (fn as any)(...deps) : fn.apply(obj, deps);
|
||||
}
|
||||
} else {
|
||||
value = parent.get(token, notFoundValue);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
function computeDeps(provider: StaticProvider): DependencyRecord[] {
|
||||
let deps: DependencyRecord[] = EMPTY;
|
||||
const providerDeps: any[] =
|
||||
(provider as ExistingProvider & StaticClassProvider & ConstructorProvider).deps;
|
||||
if (providerDeps && providerDeps.length) {
|
||||
deps = [];
|
||||
for (let i = 0; i < providerDeps.length; i++) {
|
||||
let options = OptionFlags.Default;
|
||||
let token = resolveForwardRef(providerDeps[i]);
|
||||
if (token instanceof Array) {
|
||||
for (let j = 0, annotations = token; j < annotations.length; j++) {
|
||||
const annotation = annotations[j];
|
||||
if (annotation instanceof Optional || annotation == Optional) {
|
||||
options = options | OptionFlags.Optional;
|
||||
} else if (annotation instanceof SkipSelf || annotation == SkipSelf) {
|
||||
options = options & ~OptionFlags.CheckSelf;
|
||||
} else if (annotation instanceof Self || annotation == Self) {
|
||||
options = options & ~OptionFlags.CheckParent;
|
||||
} else if (annotation instanceof Inject) {
|
||||
token = (annotation as Inject).token;
|
||||
} else {
|
||||
token = resolveForwardRef(annotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
deps.push({token, options});
|
||||
}
|
||||
} else if ((provider as ExistingProvider).useExisting) {
|
||||
const token = resolveForwardRef((provider as ExistingProvider).useExisting);
|
||||
deps = [{token, options: OptionFlags.Default}];
|
||||
} else if (!providerDeps && !(USE_VALUE in provider)) {
|
||||
// useValue & useExisting are the only ones which are exempt from deps all others need it.
|
||||
throw staticError('\'deps\' required', provider);
|
||||
}
|
||||
return deps;
|
||||
}
|
||||
|
||||
function formatError(text: string, obj: any): string {
|
||||
text = text && text.charAt(0) === '\n' && text.charAt(1) == NO_NEW_LINE ? text.substr(2) : text;
|
||||
let context = stringify(obj);
|
||||
if (obj instanceof Array) {
|
||||
context = obj.map(stringify).join(' -> ');
|
||||
} else if (typeof obj === 'object') {
|
||||
let parts = <string[]>[];
|
||||
for (let key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
let value = obj[key];
|
||||
parts.push(
|
||||
key + ':' + (typeof value === 'string' ? JSON.stringify(value) : stringify(value)));
|
||||
}
|
||||
}
|
||||
context = `{${parts.join(', ')}}`;
|
||||
}
|
||||
return `StaticInjectorError[${context}]: ${text.replace(NEW_LINE, '\n ')}`;
|
||||
}
|
||||
|
||||
function staticError(text: string, obj: any): Error {
|
||||
return new Error(formatError(text, obj));
|
||||
}
|
||||
|
||||
function getClosureSafeProperty<T>(objWithPropertyToExtract: T): string {
|
||||
for (let key in objWithPropertyToExtract) {
|
||||
if (objWithPropertyToExtract[key] === GET_PROPERTY_NAME) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
throw Error('!prop');
|
||||
}
|
||||
|
@ -8,32 +8,6 @@
|
||||
|
||||
import {Type} from '../type';
|
||||
|
||||
/**
|
||||
* @whatItDoes Configures the {@link Injector} to return an instance of `Type` when `Type' is used
|
||||
* as token.
|
||||
* @howToUse
|
||||
* ```
|
||||
* @Injectable()
|
||||
* class MyService {}
|
||||
*
|
||||
* const provider: TypeProvider = MyService;
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Create an instance by invoking the `new` operator and supplying additional arguments.
|
||||
* This form is a short form of `TypeProvider`;
|
||||
*
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/provider_spec.ts region='TypeProvider'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface TypeProvider extends Type<any> {}
|
||||
|
||||
/**
|
||||
* @whatItDoes Configures the {@link Injector} to return a value for a token.
|
||||
* @howToUse
|
||||
@ -79,7 +53,7 @@ export interface ValueProvider {
|
||||
* @Injectable()
|
||||
* class MyService {}
|
||||
*
|
||||
* const provider: ClassProvider = {provide: 'someToken', useClass: MyService};
|
||||
* const provider: ClassProvider = {provide: 'someToken', useClass: MyService, deps: []};
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
@ -87,24 +61,74 @@ export interface ValueProvider {
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/provider_spec.ts region='ClassProvider'}
|
||||
* {@example core/di/ts/provider_spec.ts region='StaticClassProvider'}
|
||||
*
|
||||
* Note that following two providers are not equal:
|
||||
* {@example core/di/ts/provider_spec.ts region='ClassProviderDifference'}
|
||||
* {@example core/di/ts/provider_spec.ts region='StaticClassProviderDifference'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface ClassProvider {
|
||||
export interface StaticClassProvider {
|
||||
/**
|
||||
* An injection token. (Typically an instance of `Type` or `InjectionToken`, but can be `any`).
|
||||
*/
|
||||
provide: any;
|
||||
|
||||
/**
|
||||
* Class to instantiate for the `token`.
|
||||
* An optional class to instantiate for the `token`. (If not provided `provide` is assumed to be a
|
||||
* class to
|
||||
* instantiate)
|
||||
*/
|
||||
useClass: Type<any>;
|
||||
|
||||
/**
|
||||
* A list of `token`s which need to be resolved by the injector. The list of values is then
|
||||
* used as arguments to the `useClass` constructor.
|
||||
*/
|
||||
deps: any[];
|
||||
|
||||
/**
|
||||
* If true, then injector returns an array of instances. This is useful to allow multiple
|
||||
* providers spread across many files to provide configuration information to a common token.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect'}
|
||||
*/
|
||||
multi?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @whatItDoes Configures the {@link Injector} to return an instance of a token.
|
||||
* @howToUse
|
||||
* ```
|
||||
* @Injectable()
|
||||
* class MyService {}
|
||||
*
|
||||
* const provider: ClassProvider = {provide: MyClass, deps: []};
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/provider_spec.ts region='ConstructorProvider'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface ConstructorProvider {
|
||||
/**
|
||||
* An injection token. (Typically an instance of `Type` or `InjectionToken`, but can be `any`).
|
||||
*/
|
||||
provide: Type<any>;
|
||||
|
||||
/**
|
||||
* A list of `token`s which need to be resolved by the injector. The list of values is then
|
||||
* used as arguments to the `useClass` constructor.
|
||||
*/
|
||||
deps: any[];
|
||||
|
||||
/**
|
||||
* If true, then injector returns an array of instances. This is useful to allow multiple
|
||||
* providers spread across many files to provide configuration information to a common token.
|
||||
@ -205,11 +229,95 @@ export interface FactoryProvider {
|
||||
multi?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @whatItDoes Describes how the {@link Injector} should be configured in a static way (Without
|
||||
* reflection).
|
||||
* @howToUse
|
||||
* See {@link ValueProvider}, {@link ExistingProvider}, {@link FactoryProvider}.
|
||||
*
|
||||
* @description
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export type StaticProvider = ValueProvider | ExistingProvider | StaticClassProvider |
|
||||
ConstructorProvider | FactoryProvider | any[];
|
||||
|
||||
|
||||
/**
|
||||
* @whatItDoes Configures the {@link Injector} to return an instance of `Type` when `Type' is used
|
||||
* as token.
|
||||
* @howToUse
|
||||
* ```
|
||||
* @Injectable()
|
||||
* class MyService {}
|
||||
*
|
||||
* const provider: TypeProvider = MyService;
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Create an instance by invoking the `new` operator and supplying additional arguments.
|
||||
* This form is a short form of `TypeProvider`;
|
||||
*
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/provider_spec.ts region='TypeProvider'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface TypeProvider extends Type<any> {}
|
||||
|
||||
/**
|
||||
* @whatItDoes Configures the {@link Injector} to return an instance of `useClass` for a token.
|
||||
* @howToUse
|
||||
* ```
|
||||
* @Injectable()
|
||||
* class MyService {}
|
||||
*
|
||||
* const provider: ClassProvider = {provide: 'someToken', useClass: MyService};
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/provider_spec.ts region='ClassProvider'}
|
||||
*
|
||||
* Note that following two providers are not equal:
|
||||
* {@example core/di/ts/provider_spec.ts region='ClassProviderDifference'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface ClassProvider {
|
||||
/**
|
||||
* An injection token. (Typically an instance of `Type` or `InjectionToken`, but can be `any`).
|
||||
*/
|
||||
provide: any;
|
||||
|
||||
/**
|
||||
* Class to instantiate for the `token`.
|
||||
*/
|
||||
useClass: Type<any>;
|
||||
|
||||
/**
|
||||
* If true, then injector returns an array of instances. This is useful to allow multiple
|
||||
* providers spread across many files to provide configuration information to a common token.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect'}
|
||||
*/
|
||||
multi?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @whatItDoes Describes how the {@link Injector} should be configured.
|
||||
* @howToUse
|
||||
* See {@link TypeProvider}, {@link ValueProvider}, {@link ClassProvider}, {@link ExistingProvider},
|
||||
* {@link FactoryProvider}.
|
||||
* See {@link TypeProvider}, {@link ClassProvider}, {@link StaticProvider}.
|
||||
*
|
||||
* @description
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
|
@ -13,6 +13,8 @@ import {cyclicDependencyError, instantiationError, noProviderError, outOfBoundsE
|
||||
import {ReflectiveKey} from './reflective_key';
|
||||
import {ReflectiveDependency, ResolvedReflectiveFactory, ResolvedReflectiveProvider, resolveReflectiveProviders} from './reflective_provider';
|
||||
|
||||
|
||||
|
||||
// Threshold for the dynamic version
|
||||
const UNDEFINED = new Object();
|
||||
|
||||
|
@ -24,7 +24,7 @@ import {resolveForwardRef} from './forward_ref';
|
||||
* `Key` should not be created directly. {@link ReflectiveInjector} creates keys automatically when
|
||||
* resolving
|
||||
* providers.
|
||||
* @experimental
|
||||
* @deprecated No replacement
|
||||
*/
|
||||
export class ReflectiveKey {
|
||||
/**
|
||||
|
@ -60,6 +60,10 @@ export function stringify(token: any): string {
|
||||
return token;
|
||||
}
|
||||
|
||||
if (token instanceof Array) {
|
||||
return '[' + token.map(stringify).join(', ') + ']';
|
||||
}
|
||||
|
||||
if (token == null) {
|
||||
return '' + token;
|
||||
}
|
||||
|
Reference in New Issue
Block a user