runtime-branding
Version:
Runtime Branding API for TypeScript
90 lines (89 loc) • 4.42 kB
TypeScript
declare const brand: unique symbol;
declare const targetType: unique symbol;
export interface Brand<T, B> {
[targetType]?: T;
[brand]: B;
}
/**
* Branded object type
*/
export declare type Branded<T, B> = [T & Brand<T, B>][0];
declare type BrandedObject<B extends object, X extends object> = (object extends X ? [Brand<X, B>][0] : [X & Branded<X, B>][0]);
/**
* Branding function definition
*/
export interface Branding<B extends object, X extends object = object> extends Brand<X, B> {
/**
* Brands an object
* @param obj the object to brand
* @throws Error if the object is already branded
*/
<T extends X>(obj: T): Branded<T, B>;
/**
* Asserts this brand for the given object.
* @param obj the object to assert brand on
* @throws Error when object is not branded.
*/
assert(obj: object): asserts obj is BrandedObject<B, X>;
/**
* Checks if the given object has this brand.
* @param obj the object to check
*/
has(obj: object): obj is BrandedObject<B, X>;
/**
* Refines this branding with a new outer brand. The resulting branding will
* be a composition (merge) between the inner and outer brand.
* This method is actually a shortcut for createBranding and subsequent merge.
*
* @param newBrand the new brand that refines the current one
* @param callback a callback invoked when object are branded with the refined branding
*/
refine<N extends object, Y extends X = X>(newBrand: N, callback?: BrandingCallback<N, Y>): Branding<N & B, Y>;
/**
* Merges the given branding with the current one.
*
* @param branding the other brand to merge with
*/
merge<C extends object, Y extends X = X>(branding: Branding<C, Y>): Branding<B & C, Y>;
}
/**
* A branding callback invoked during object branding
*/
export declare type BrandingCallback<B extends object, T extends object = object> = (obj: T, brand: B) => void;
/**
* Creates a new brand, identified by the provided branding object.
*
* @param brandObject An object that represents the brand. The shape of this object should be unique, and symbols may be used as unique keys.
* @param callback An optional callback with variable arguments which is invoked during object branding
*/
export declare function createBranding<B extends object, X extends object = object>(brandObject: B, callback?: BrandingCallback<B, X>): Branding<B, X>;
/***********************************************/
export declare type Constructor<T, A extends any[]> = new (...args: A) => T;
export declare type TypeId<T> = string | Constructor<T, any[]>;
declare const typeSym: unique symbol;
export interface TypeBrand<T extends object> {
readonly [typeSym]: null;
readonly typeId: TypeId<T>;
readonly base: Set<TypeBranding<object>>;
readonly ancestors: Set<TypeBranding<object>>;
}
export declare type Typed<T extends object> = Branded<T, TypeBrand<T>>;
export interface TypeBranding<T extends object> extends Branding<TypeBrand<T>, T>, TypeBrand<T> {
(obj: T): Typed<T>;
}
export interface BaseTypeBranding<T extends object> {
<S extends object>(obj: T extends infer S ? S : never): Branded<any, TypeBrand<S>>;
assert: Function;
has: Function;
refine: Function;
merge: Function;
}
export declare function type<T = void>(typeId: string, ...base: T extends object ? BaseTypeBranding<T>[] : never): T extends object ? TypeBranding<T> : never;
export declare function type<T extends object>(typeId: Constructor<T, any[]>, ...base: BaseTypeBranding<T>[]): TypeBranding<T>;
export declare function typeOf<T extends Constructor<any, any>>(obj: T): typeof obj extends Constructor<infer X, any[]> ? X extends object ? TypeBranding<X> : never : never;
export declare function typeOf<T>(obj: T): typeof obj extends InstanceType<Constructor<infer X, any>> ? X extends object ? TypeBranding<X> : never : TypeBranding<object>;
export declare function typeOf<T extends object>(obj: T): T extends Typed<infer X> ? TypeBranding<X> : TypeBranding<object>;
export declare function isTyped(obj: object): boolean;
export declare function assertType<X, T extends Object>(type: TypeBranding<T>, obj: X): asserts obj is Typed<T & X>;
export declare function isTypeBranding(fn: any): fn is TypeBranding<object>;
export {};