feat(core): Add type information to injector.get() (#13785)
- Introduce `InjectionToken<T>` which is a parameterized and type-safe version of `OpaqueToken`. DEPRECATION: - `OpaqueToken` is now deprecated, use `InjectionToken<T>` instead. - `Injector.get(token: any, notFoundValue?: any): any` is now deprecated use the same method which is now overloaded as `Injector.get<T>(token: Type<T>|InjectionToken<T>, notFoundValue?: T): T;`. Migration - Replace `OpaqueToken` with `InjectionToken<?>` and parameterize it. - Migrate your code to only use `Type<?>` or `InjectionToken<?>` as injection tokens. Using other tokens will not be supported in the future. BREAKING CHANGE: - Because `injector.get()` is now parameterize it is possible that code which used to work no longer type checks. Example would be if one injects `Foo` but configures it as `{provide: Foo, useClass: MockFoo}`. The injection instance will be that of `MockFoo` but the type will be `Foo` instead of `any` as in the past. This means that it was possible to call a method on `MockFoo` in the past which now will fail type check. See this example: ``` class Foo {} class MockFoo extends Foo { setupMock(); } var PROVIDERS = [ {provide: Foo, useClass: MockFoo} ]; ... function myTest(injector: Injector) { var foo = injector.get(Foo); // This line used to work since `foo` used to be `any` before this // change, it will now be `Foo`, and `Foo` does not have `setUpMock()`. // The fix is to downcast: `injector.get(Foo) as MockFoo`. foo.setUpMock(); } ``` PR Close #13785
This commit is contained in:

committed by
Miško Hevery

parent
6d1f1a43bb
commit
d169c2434e
@ -26,10 +26,42 @@
|
||||
*
|
||||
* Using an `OpaqueToken` is preferable to using an `Object` as tokens because it provides better
|
||||
* error messages.
|
||||
* @stable
|
||||
* @deprecated since v4.0.0 because it does not support type information, use `InjectionToken<?>`
|
||||
* instead.
|
||||
*/
|
||||
export class OpaqueToken {
|
||||
constructor(private _desc: string) {}
|
||||
constructor(protected _desc: string) {}
|
||||
|
||||
toString(): string { return `Token ${this._desc}`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a token that can be used in a DI Provider.
|
||||
*
|
||||
* Use an `InjectionToken` whenever the type you are injecting is not reified (does not have a
|
||||
* runtime representation) such as when injecting an interface, callable type, array or
|
||||
* parametrized type.
|
||||
*
|
||||
* `InjectionToken` is parametrize on `T` which is the type of object which will be returned by the
|
||||
* `Injector`. This provides additional level of type safety.
|
||||
*
|
||||
* ```
|
||||
* interface MyInterface {...}
|
||||
* var myInterface = injector.get(new InjectionToken<MyInterface>('SomeToken'));
|
||||
* // myInterface is inferred to be MyInterface.
|
||||
* ```
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/injector_spec.ts region='Injector'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export class InjectionToken<T> extends OpaqueToken {
|
||||
// This unused property is needed here so that TS can differentiate InjectionToken from
|
||||
// OpaqueToken since otherwise they would have the same shape and be treated as equivalent.
|
||||
private _differentiate_from_OpaqueToken_structurally: any;
|
||||
constructor(desc: string) { super(desc); }
|
||||
|
||||
toString(): string { return `InjectionToken ${this._desc}`; }
|
||||
}
|
||||
|
@ -8,6 +8,9 @@
|
||||
|
||||
import {unimplemented} from '../facade/errors';
|
||||
import {stringify} from '../facade/lang';
|
||||
import {Type} from '../type';
|
||||
|
||||
import {InjectionToken} from './injection_token';
|
||||
|
||||
const _THROW_IF_NOT_FOUND = new Object();
|
||||
export const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
|
||||
@ -52,5 +55,10 @@ export abstract class Injector {
|
||||
* Injector.THROW_IF_NOT_FOUND is given
|
||||
* - Returns the `notFoundValue` otherwise
|
||||
*/
|
||||
get<T>(token: Type<T>|InjectionToken<T>, notFoundValue?: T): T;
|
||||
/**
|
||||
* @deprecated from v4.0.0 use Type<T> or InjectToken<T>
|
||||
*/
|
||||
get(token: any, notFoundValue?: any): any;
|
||||
get(token: any, notFoundValue?: any): any { return unimplemented(); }
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ export interface TypeProvider extends Type<any> {}
|
||||
*/
|
||||
export interface ValueProvider {
|
||||
/**
|
||||
* An injection token. (Typically an instance of `Type` or `OpaqueToken`, but can be `any`).
|
||||
* An injection token. (Typically an instance of `Type` or `InjectionToken`, but can be `any`).
|
||||
*/
|
||||
provide: any;
|
||||
|
||||
@ -96,7 +96,7 @@ export interface ValueProvider {
|
||||
*/
|
||||
export interface ClassProvider {
|
||||
/**
|
||||
* An injection token. (Typically an instance of `Type` or `OpaqueToken`, but can be `any`).
|
||||
* An injection token. (Typically an instance of `Type` or `InjectionToken`, but can be `any`).
|
||||
*/
|
||||
provide: any;
|
||||
|
||||
@ -134,7 +134,7 @@ export interface ClassProvider {
|
||||
*/
|
||||
export interface ExistingProvider {
|
||||
/**
|
||||
* An injection token. (Typically an instance of `Type` or `OpaqueToken`, but can be `any`).
|
||||
* An injection token. (Typically an instance of `Type` or `InjectionToken`, but can be `any`).
|
||||
*/
|
||||
provide: any;
|
||||
|
||||
@ -178,7 +178,7 @@ export interface ExistingProvider {
|
||||
*/
|
||||
export interface FactoryProvider {
|
||||
/**
|
||||
* An injection token. (Typically an instance of `Type` or `OpaqueToken`, but can be `any`).
|
||||
* An injection token. (Typically an instance of `Type` or `InjectionToken`, but can be `any`).
|
||||
*/
|
||||
provide: any;
|
||||
|
||||
|
Reference in New Issue
Block a user