-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
249 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,7 @@ npm-debug.log | |
# Build directories | ||
dist | ||
build | ||
build-cli | ||
.angular/cache | ||
|
||
# Testing | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { InjectionToken } from "@angular/core"; | ||
|
||
import { StorageService } from "../../../jslib/common/src/abstractions/storage.service"; | ||
|
||
declare const tag: unique symbol; | ||
/** | ||
* A (more) typesafe version of InjectionToken which will more strictly enforce the generic type parameter. | ||
* @remarks The default angular implementation does not use the generic type to define the structure of the object, | ||
* so the structural type system will not complain about a mismatch in the type parameter. | ||
* This is solved by assigning T to an arbitrary private property. | ||
*/ | ||
export class SafeInjectionToken<T> extends InjectionToken<T> { | ||
private readonly [tag]: T; | ||
} | ||
|
||
export const SECURE_STORAGE = new SafeInjectionToken<StorageService>("SECURE_STORAGE"); | ||
export const WINDOW = new SafeInjectionToken<Window>("WINDOW"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
import { Provider } from "@angular/core"; | ||
import { Constructor, Opaque } from "type-fest"; | ||
|
||
import { SafeInjectionToken } from "./injection-tokens"; | ||
|
||
// ****** | ||
// NOTE: this is a copy/paste of safe-provider.ts from the clients repository. | ||
// The clients repository remains the primary version of this code. | ||
// Make any changes there and copy it back to this repository. | ||
// ****** | ||
|
||
/** | ||
* The return type of the {@link safeProvider} helper function. | ||
* Used to distinguish a type safe provider definition from a non-type safe provider definition. | ||
*/ | ||
export type SafeProvider = Opaque<Provider>; | ||
|
||
// TODO: type-fest also provides a type like this when we upgrade >= 3.7.0 | ||
type AbstractConstructor<T> = abstract new (...args: any) => T; | ||
|
||
type MapParametersToDeps<T> = { | ||
[K in keyof T]: AbstractConstructor<T[K]> | SafeInjectionToken<T[K]>; | ||
}; | ||
|
||
type SafeInjectionTokenType<T> = T extends SafeInjectionToken<infer J> ? J : never; | ||
|
||
/** | ||
* Gets the instance type from a constructor, abstract constructor, or SafeInjectionToken | ||
*/ | ||
type ProviderInstanceType<T> = | ||
T extends SafeInjectionToken<any> | ||
? InstanceType<SafeInjectionTokenType<T>> | ||
: T extends Constructor<any> | AbstractConstructor<any> | ||
? InstanceType<T> | ||
: never; | ||
|
||
/** | ||
* Represents a dependency provided with the useClass option. | ||
*/ | ||
type SafeClassProvider< | ||
A extends AbstractConstructor<any> | SafeInjectionToken<any>, | ||
I extends Constructor<ProviderInstanceType<A>>, | ||
D extends MapParametersToDeps<ConstructorParameters<I>>, | ||
> = { | ||
provide: A; | ||
useClass: I; | ||
deps: D; | ||
}; | ||
|
||
/** | ||
* Represents a dependency provided with the useValue option. | ||
*/ | ||
type SafeValueProvider<A extends SafeInjectionToken<any>, V extends SafeInjectionTokenType<A>> = { | ||
provide: A; | ||
useValue: V; | ||
}; | ||
|
||
/** | ||
* Represents a dependency provided with the useFactory option. | ||
*/ | ||
type SafeFactoryProvider< | ||
A extends AbstractConstructor<any> | SafeInjectionToken<any>, | ||
I extends (...args: any) => ProviderInstanceType<A>, | ||
D extends MapParametersToDeps<Parameters<I>>, | ||
> = { | ||
provide: A; | ||
useFactory: I; | ||
deps: D; | ||
multi?: boolean; | ||
}; | ||
|
||
/** | ||
* Represents a dependency provided with the useExisting option. | ||
*/ | ||
type SafeExistingProvider< | ||
A extends Constructor<any> | AbstractConstructor<any> | SafeInjectionToken<any>, | ||
I extends Constructor<ProviderInstanceType<A>> | AbstractConstructor<ProviderInstanceType<A>>, | ||
> = { | ||
provide: A; | ||
useExisting: I; | ||
}; | ||
|
||
/** | ||
* Represents a dependency where there is no abstract token, the token is the implementation | ||
*/ | ||
type SafeConcreteProvider< | ||
I extends Constructor<any>, | ||
D extends MapParametersToDeps<ConstructorParameters<I>>, | ||
> = { | ||
provide: I; | ||
deps: D; | ||
}; | ||
|
||
/** | ||
* If useAngularDecorators: true is specified, do not require a deps array. | ||
* This is a manual override for where @Injectable decorators are used | ||
*/ | ||
type UseAngularDecorators<T extends { deps: any }> = Omit<T, "deps"> & { | ||
useAngularDecorators: true; | ||
}; | ||
|
||
/** | ||
* Represents a type with a deps array that may optionally be overridden with useAngularDecorators | ||
*/ | ||
type AllowAngularDecorators<T extends { deps: any }> = T | UseAngularDecorators<T>; | ||
|
||
/** | ||
* A factory function that creates a provider for the ngModule providers array. | ||
* This (almost) guarantees type safety for your provider definition. It does nothing at runtime. | ||
* Warning: the useAngularDecorators option provides an override where your class uses the Injectable decorator, | ||
* however this cannot be enforced by the type system and will not cause an error if the decorator is not used. | ||
* @example safeProvider({ provide: MyService, useClass: DefaultMyService, deps: [AnotherService] }) | ||
* @param provider Your provider object in the usual shape (e.g. using useClass, useValue, useFactory, etc.) | ||
* @returns The exact same object without modification (pass-through). | ||
*/ | ||
export const safeProvider = < | ||
// types for useClass | ||
AClass extends AbstractConstructor<any> | SafeInjectionToken<any>, | ||
IClass extends Constructor<ProviderInstanceType<AClass>>, | ||
DClass extends MapParametersToDeps<ConstructorParameters<IClass>>, | ||
// types for useValue | ||
AValue extends SafeInjectionToken<any>, | ||
VValue extends SafeInjectionTokenType<AValue>, | ||
// types for useFactory | ||
AFactory extends AbstractConstructor<any> | SafeInjectionToken<any>, | ||
IFactory extends (...args: any) => ProviderInstanceType<AFactory>, | ||
DFactory extends MapParametersToDeps<Parameters<IFactory>>, | ||
// types for useExisting | ||
AExisting extends Constructor<any> | AbstractConstructor<any> | SafeInjectionToken<any>, | ||
IExisting extends | ||
| Constructor<ProviderInstanceType<AExisting>> | ||
| AbstractConstructor<ProviderInstanceType<AExisting>>, | ||
// types for no token | ||
IConcrete extends Constructor<any>, | ||
DConcrete extends MapParametersToDeps<ConstructorParameters<IConcrete>>, | ||
>( | ||
provider: | ||
| AllowAngularDecorators<SafeClassProvider<AClass, IClass, DClass>> | ||
| SafeValueProvider<AValue, VValue> | ||
| AllowAngularDecorators<SafeFactoryProvider<AFactory, IFactory, DFactory>> | ||
| SafeExistingProvider<AExisting, IExisting> | ||
| AllowAngularDecorators<SafeConcreteProvider<IConcrete, DConcrete>> | ||
| Constructor<unknown>, | ||
): SafeProvider => provider as SafeProvider; |
Oops, something went wrong.