Created
July 8, 2022 09:05
-
-
Save SuperOleg39/93e1aedbe01ed09a94a65851f9fb94ab to your computer and use it in GitHub Desktop.
Example code with token scope type checking
This file contains hidden or 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
| export const Scope = { | |
| REQUEST: 'request' as const, | |
| SINGLETON: 'singleton' as const, | |
| }; | |
| export const Errors = { | |
| NOT_FOUND: 'NotFound', | |
| CIRCULAR_DEP: 'CircularDep', | |
| REQUIRE_MULTI: 'RequireMulti', | |
| MIXED_MULTI: 'MixedMulti', | |
| WRONG_FORMAT: 'WrongFormat', | |
| }; |
This file contains hidden or 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
| import type { Scope } from '../constant'; | |
| export interface TokenType<T> { | |
| name: string; | |
| options: TokenOptions; | |
| isToken: true; | |
| isModernToken: true; | |
| toString(): string; | |
| } | |
| export interface TokenOptions { | |
| multi?: boolean; | |
| scope?: typeof Scope[keyof typeof Scope]; | |
| } |
This file contains hidden or 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
| // import type { ScopeVariants } from '../Provider'; | |
| import type { Scope } from '../constant'; | |
| import type { TokenType, TokenOptions } from './createToken.h'; | |
| export class TokenClass<T> implements TokenType<T> { | |
| /** | |
| * Индетификатор токена | |
| */ | |
| name: string; | |
| options: TokenOptions; | |
| isToken: true = true; | |
| // for potential breaking changes, not very useful at this moment | |
| isModernToken: true = true; | |
| constructor(name: string, options: TokenOptions = {}) { | |
| this.name = name; | |
| this.options = options; | |
| } | |
| /** | |
| * toString будет использоваться для получения индитификатора токена | |
| */ | |
| toString() { | |
| return this.name; | |
| } | |
| } | |
| const BASE_TOKEN_TYPE = 'base token'; | |
| const MULTI_TOKEN_TYPE = 'multi token'; | |
| export type _BaseTokenRequestInterface = { | |
| __type?: typeof BASE_TOKEN_TYPE; | |
| __scope?: typeof Scope['REQUEST']; | |
| }; | |
| export type _BaseTokenSingletonInterface = { | |
| __type?: typeof BASE_TOKEN_TYPE; | |
| __scope?: typeof Scope['SINGLETON']; | |
| }; | |
| export type BaseTokenInterface<T = any> = T & | |
| (_BaseTokenRequestInterface | _BaseTokenSingletonInterface); | |
| export type MultiTokenInterface<T = any> = T & { | |
| __type?: typeof MULTI_TOKEN_TYPE; | |
| }; | |
| export type TokenInterface<T = any> = BaseTokenInterface<T> | MultiTokenInterface<T>; | |
| export function createToken<Type = any>( | |
| name: string, | |
| options: { scope: typeof Scope['REQUEST'] } | |
| ): Type & _BaseTokenRequestInterface; | |
| export function createToken<Type = any>( | |
| name: string, | |
| options: { scope: typeof Scope['SINGLETON'] } | |
| ): Type & _BaseTokenSingletonInterface; | |
| export function createToken<Type = any>( | |
| name: string, | |
| options?: { scope?: typeof Scope[keyof typeof Scope] } | |
| ): BaseTokenInterface<Type>; | |
| export function createToken<Type = any>( | |
| name: string, | |
| options: { multi: true; scope?: typeof Scope[keyof typeof Scope] } | |
| ): MultiTokenInterface<Type>; | |
| export function createToken<T = any>(name: string, options?: TokenOptions): T { | |
| return (new TokenClass<T>(name, options) as any) as T; | |
| } | |
| export type ExtractTokenType< | |
| Token extends TokenInterface<unknown> | |
| > = Token extends MultiTokenInterface<infer Type> | |
| ? Type[] | |
| : Token extends BaseTokenInterface<infer Type> | |
| ? Type | |
| : unknown; | |
| export type OptionalTokenDependency<Type extends unknown> = { | |
| token: TokenInterface<Type>; | |
| optional: boolean; | |
| }; |
This file contains hidden or 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
| import type { | |
| TokenInterface, | |
| MultiTokenInterface, | |
| ExtractTokenType, | |
| OptionalTokenDependency, | |
| _BaseTokenSingletonInterface, | |
| _BaseTokenRequestInterface, | |
| } from './createToken/createToken'; | |
| type Provide = TokenInterface<unknown> | string | any; | |
| export type ScopeVariants = 'request' | 'singleton'; | |
| type ProviderOptions = { token: Provide; optional?: boolean; multi?: boolean }; | |
| export type ProviderDep = Provide | OptionalTokenDependency<unknown> | ProviderOptions; | |
| export type ProviderDeps = Record<string, ProviderDep>; | |
| // если есть multi параметр, то это массив данных | |
| type MultiEnhance<Option, Value> = Option extends true ? Value[] : Value; | |
| // если есть optional параметр, то мы должны давать или значение или null. Работает только в strict режиме | |
| type OptionalEnhance<Option, Value> = Option extends true ? Value | null : Value; | |
| // обрабатываем options тип | |
| export type OptionsType<OptionsToken, OptionsMulti, OptionsOptional> = OptionsToken extends string | |
| ? OptionalEnhance<OptionsOptional, MultiEnhance<OptionsMulti, any>> | |
| : OptionalEnhance<OptionsOptional, MultiEnhance<OptionsMulti, OptionsToken>>; | |
| // prettier-ignore | |
| export type ProvideDepsIterator<T> = { | |
| [P in keyof T]: T[P] extends OptionalTokenDependency<unknown> | |
| ? (ExtractTokenType<T[P]['token']> | null) | |
| : T[P] extends TokenInterface<unknown> | |
| ? ExtractTokenType<T[P]> | |
| : T[P] extends string | |
| ? any // строковые токены = any | |
| : T[P] extends { token: infer OptionsToken; optional?: infer OptionsOptional, multi?: infer OptionsMulti } | |
| ? OptionsType<OptionsToken, OptionsMulti, OptionsOptional> | |
| : T[P]; // Обычный токен | |
| }; | |
| // prettier-ignore | |
| type ClassCreator<Deps, P extends Provide = any> = new ( | |
| deps: ProvideDepsIterator<Deps> | |
| ) => P extends TokenInterface<unknown> | |
| ? P extends MultiTokenInterface<unknown> | |
| ? ExtractTokenType<P> | ExtractTokenType<P>[number] | |
| : ExtractTokenType<P> | |
| : P extends string | |
| ? any | |
| : P; | |
| // prettier-ignore | |
| type FactoryCreator<Deps, P extends Provide = any> = ( | |
| deps: ProvideDepsIterator<FilterRequestScopeFromSingletonDeps<Deps, P>> | |
| ) => P extends TokenInterface<unknown> | |
| ? P extends MultiTokenInterface<unknown> | |
| ? ExtractTokenType<P> | ExtractTokenType<P>[number] | |
| : ExtractTokenType<P> | |
| : P extends string | |
| ? any | |
| : P; | |
| type CompileError<ErrorMessage extends any[]> = { | |
| readonly __compileError: never; | |
| }; | |
| // prettier-ignore | |
| type FilterRequestScopeFromSingletonDeps< | |
| Deps, | |
| P extends Provide = any | |
| > = P extends _BaseTokenSingletonInterface | |
| ? { | |
| [Key in keyof Deps]: Deps[Key] extends _BaseTokenRequestInterface | |
| ? CompileError<["Dependency", Key ,"with scope: 'request' is not allowed in deps to scope: 'singleton' provider"]> | |
| : Deps[Key] extends { token: _BaseTokenRequestInterface } | |
| ? CompileError<["Dependency", Key ,"with scope: 'request' is not allowed in deps to scope: 'singleton' provider"]> | |
| : Deps[Key]; | |
| } | |
| : Deps; | |
| export interface FactoryProvider<Deps, P extends Provide = any> { | |
| /** | |
| * Идентификатор токена | |
| */ | |
| provide: P extends TokenInterface<unknown> ? P : Provide; | |
| /** | |
| * Тип регистрации провайдера, будет ли глобальный синглтон или инстанс для клиента | |
| */ | |
| scope?: ScopeVariants; | |
| /** | |
| * Список токенов которые нужны сервису. При получении зависимости | |
| * эти зависимости будут проинициализованы и переданны в функцию/класс | |
| */ | |
| deps?: Deps; | |
| /** | |
| * Если true, то позволяет зарегистрировать несколько провайдеров на одном токен. Придет массив как значение | |
| */ | |
| multi?: boolean; | |
| /** | |
| * Функция которая будет вызвана при инициализации провайдера и получит зависимости, если они были заданы | |
| */ | |
| useFactory: FactoryCreator<Deps, P>; | |
| } | |
| export interface ClassProvider<Deps, P extends Provide = any> { | |
| /** | |
| * Идентификатор токена | |
| */ | |
| provide: P extends TokenInterface<unknown> ? P : Provide; | |
| /** | |
| * Тип регистрации провайдера, будет ли глобальный синглтон или инстанс для клиента | |
| */ | |
| scope?: ScopeVariants; | |
| /** | |
| * Список токенов которые нужны сервису. При получении зависимости | |
| * эти зависимости будут проинициализованы и переданны в функцию/класс | |
| */ | |
| deps?: Deps; | |
| /** | |
| * Если true, то позволяет зарегистрировать несколько провайдеров на одном токен. Придет массив как значение | |
| */ | |
| multi?: boolean; | |
| /** | |
| * Класс который будет создана при инициализации провайдера и получит зависимости, если они были заданы | |
| */ | |
| useClass: ClassCreator<Deps, P>; | |
| } | |
| export interface ValueProvider<P extends Provide = any> { | |
| /** | |
| * Идентификатор токена | |
| */ | |
| provide: P extends TokenInterface<unknown> ? P : Provide; | |
| /** | |
| * Тип регистрации провайдера, будет ли глобальный синглтон или инстанс для клиента | |
| */ | |
| scope?: ScopeVariants; | |
| /** | |
| * Если true, то позволяет зарегистрировать несколько провайдеров на одном токен. Придет массив как значение | |
| */ | |
| multi?: boolean; | |
| /** | |
| * Простое значение, которое будет доступно | |
| */ | |
| // prettier-ignore | |
| useValue: P extends TokenInterface<unknown> | |
| ? P extends MultiTokenInterface<unknown> | |
| ? ExtractTokenType<P> | ExtractTokenType<P>[number] | |
| : ExtractTokenType<P> | |
| : P extends string | |
| ? any | |
| : P; | |
| } | |
| export type Provider<Deps = any, P extends Provide = any> = | |
| | ValueProvider<P> | |
| | ClassProvider<Deps, P> | |
| | FactoryProvider<Deps, P>; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment