import { Identity , Func , identityFunc , GetDataType , ExtractOfUnion , strEnum , isPromise } from './util' ; /**
 * A type representing an object with a single property.
 *  - `Property<K, T>` evaluates to `{ [K: string]: T }`
 */ export type Property < K extends string , T > = K extends keyof infer LitK ? { [ P in keyof LitK ] : T ; } : never ; deprecated export type WithProperty < K extends string , T > = Property < K , T > ; /**
 * Alias of `Property<K extends string, T>`
 */ export type TypeExt < K extends string , T > = Property < K , T > ; /**
 * The type marking metadata. It's useful to know the type of the items the function will generate.
 *
 * It doesn't *really* matter if it's creator.outputType vs. creator.type, but
 * the latter has the advantage of being tolerable to the group of people who will
 * prefer to use [Animal.dog.type]: rather than dog: .
 */ export type Outputs < K , T > = { /**
     * Discriminant property key
     */ key : K ; /**
     * The type of object created by this function.
     */ type : T ; } ; /**
 * More specific toString();
 */ export type Stringable < ReturnType extends string > = { toString ( ) : ReturnType ; } ; /**
 * The constructor for one tag of a variant type.
 *
 *  - `T` extends `string` — The literal string used as the type
 *  - `F` extends `(...args: any[]) => {}` = `() => {}` — The function that serves as the variant's *body definition*
 *  - `K` extends `string` = `'type'` — The discriminant
 */ export type VariantCreator < T extends string , F extends ( ... args : any [ ] ) => { } = ( ) => { } , K extends string = 'type' > = ( ( ... args : Parameters < F > ) => PatchObjectOrPromise < ReturnType < F > , Property < K , T > > ) & Outputs < K , T > & Stringable < T > ; /**
 * Given an object or a promise containing an object, patch it to
 * include some extra properties.
 *
 * This is mostly used to merge the `{type: ______}` property into
 * the body definition of a variant.
 */ export type PatchObjectOrPromise < T extends { } | PromiseLike < { } > , U extends { } > = T extends PromiseLike < infer R > ? PromiseLike < Identity < U & R > > : Identity < U & T > ; /**
 * A variant module definition. Literally an object to group together
 * a set of variant constructors.
 */ export type VariantModule < K extends string = 'type' > = { [ name ] : VariantCreator < string , ( ... args : any [ ] ) => any , K > ; } ; param key export function variantFactory < K extends string > ( key ) { param tag returns function variantFunc < T extends string > ( tag ) ; param tag param func returns function variantFunc < T extends string , F extends Func > ( tag , func ) ; function variantFunc < T extends string , F extends Func > ( tag , func ? ) { let maker = ( ... args ) => { const returned = ( func ?? identityFunc ) ( ... args ) ; if ( isPromise ( returned ) ) { return returned . then ( result => ( { [ key ] : tag , ... result , } ) ) ; } else { return { [ key ] : tag , ... returned , } ; } } ; const outputs = { key , type : tag , } ; return Object . assign ( maker , outputs , { toString : function ( this ) { return this . type ; } } ) ; } const outputKey = { outputKey : key } ; return Object . assign ( variantFunc , outputKey ) ; } export const variant = variantFactory ( 'type' ) ; export default variant ; export type Creators < VM extends VariantModule < K > , K extends string = 'type' > = { [ P in keyof VM ] : VM [ P ] extends VariantCreator < string , Func , K > ? VM [ P ] : never ; } ; /**
 * Used in writing cases of a type-first variant.
 *
 * `Variant<'One', {a: number, b: string}>>` generates
 *  - `{type: 'One', a: number, b: string}`
 *
 * You may write the literals directly, using this is recommended
 * if you'd like to update the literal as this library updates.
 */ export type Variant < Type extends string , Fields extends { } = { } , Key extends string = 'type' > = Property < Key , Type > & Fields ; type InternalVariantsOf < VM extends VariantModule < K > , K extends string = 'type' > = GetDataType < Creators < VM , K > , K > ; /**
 * Reduce an object to just elements that are `VariantCreator`s
 */ type FilterVariants < T , Type extends string , K extends string = any > = T extends VariantCreator < Type , Func , K > ? T : never ; /**
 * A valid entry for `variantList`
 */ type validListType = VariantCreator < any , Func , any > | string ; /**
 * Convert entries for a `variantList` to the same type.
 */ type Variantify < T extends validListType > = T extends string ? VariantCreator < T > : T ; export type VariantModuleFromList < T extends validListType > = { [ P in Variantify < T > [ 'type' ] ] : FilterVariants < Variantify < T > , P > ; } ; remarks param variants export function variantList < T extends validListType > ( variants ) { return variants . map ( ( v ) : VariantCreator < string > => { if ( typeof v === 'string' ) { return variant ( v ) ; } else { return v as any ; } } ) . reduce ( ( o , v ) => ( { ... o , [ v . type ] : v , } ) , Object . create ( null ) ) ; } function safeKeys < O extends { } > ( o ) { return Object . keys ( o ) as ( keyof O & string ) [ ] ; } /**
 * Input type for VariantModule
 */ export type RawVariant = { [ type ] : Func | { } ; } ; export type ConstrainedRawVariant < F extends Func > = { [ type ] : ( ... args : [ ... Parameters < F > , ... any [ ] ] ) => ReturnType < F > ; } ; export type PatternedRawVariant < F extends Func > = { [ type ] : F ; } ; /**
 * Patched Constrained Raw Variant
 */ type PatchedCRV < T extends ConstrainedRawVariant < F > , F extends Func > = { [ P in keyof T ] : ( ... args : Parameters < T [ P ] > ) => Identity < ReturnType < T [ P ] > & ReturnType < F > > ; } ; type CleanResult < T , U > = T extends undefined ? U : T extends Func ? T : T extends object ? U : T ; type FullyFuncRawVariant < V extends RawVariant > = { [ P in keyof V & string ] : CleanResult < V [ P ] , ( ) => { } > ; } ; export type OutVariant < T extends RawVariant > = { [ P in ( keyof T & string ) ] : VariantCreator < P , CleanResult < T [ P ] , ( ) => { } > > ; } ; param v export function variantModule < T extends RawVariant > ( v ) { return safeKeys ( v ) . reduce ( ( acc , key ) => { return { ... acc , [ key ] : variant ( key , typeof v [ key ] === 'function' ? v [ key ] as Func : identityFunc ) , } ; } , { } as Identity < OutVariant < T > > ) ; } export function constrained < T extends ConstrainedRawVariant < F > , F extends Func > ( _constraint_ , v ) { return v as PatchedCRV < T , F > ; } export function patterned < T extends PatternedRawVariant < F > , F extends Func > ( _constraint_ , v ) { return v as PatchedCRV < T , F > ; } /**
 * Take a variant, including some potential `{}` cases
 * and generate an object with those replaced with the `noop` function.
 */ function funcifyRawVariant < V extends RawVariant > ( v ) { return safeKeys ( v ) . reduce ( ( acc , cur ) => { return { ... acc , [ cur ] : typeof v [ cur ] === 'function' ? v [ cur ] : ( ) => { } , } ; } , { } ) as FullyFuncRawVariant < V > ; } export type AugmentedRawVariant < V extends RawVariant , F extends Func > = { [ P in keyof V & string ] : ( ... args : Parameters < FullyFuncRawVariant < V > [ P ] > ) => ( ReturnType < F > & ReturnType < FullyFuncRawVariant < V > [ P ] > ) ; } ; param variantDef param f export function augmented < T extends RawVariant , F extends ( x : OutVariant < T > ) => any > ( f , variantDef ) { const funkyDef = funcifyRawVariant ( variantDef ) ; return safeKeys ( funkyDef ) . reduce ( ( acc , key ) => { return { ... acc , [ key ] : ( ... args ) => { const item = funkyDef [ key ] ( ... args ) ; return { ... f ( item ) , ... item , } ; } } ; } , { } ) as AugmentedRawVariant < T , F > ; } param variantObject export function outputTypes < T extends { [ name ] : Outputs < string , string > ; } > ( variantObject ) { return Object . keys ( variantObject ) . map ( key => variantObject [ key ] . type ) ; } param content param key export function types < T extends VariantModule < K > , K extends string = 'type' > ( content , key ? ) ; param content param key export function types < T extends Property < K , string > , K extends string = 'type' > ( content , key ? ) ; export function types < K extends string = 'type' > ( content , key ? ) { const typeStr = key ?? 'type' as K ; if ( Array . isArray ( content ) ) { return content . map ( c => c [ typeStr ] ) ; } else { return Object . values ( content ) . map ( c => c . type ) ; } } remarks param instance param variant { T extends VariantModule < K > } param typeKey returns export function isOfVariant < T extends VariantModule < K > , K extends string = 'type' > ( instance , variant , typeKey ? ) { return instance != undefined && outputTypes ( variant ) . some ( type => type === ( instance as any ) [ typeKey ?? 'type' ] ) ; } param variants function progression < T extends VariantCreator < any , Func , any > > ( variants ) { return variants . reduce ( ( o , v ) => ( { ... o , [ v . type ] : v , } ) , Object . create ( null ) ) ; } /**
 * Same as handler but needs to handle literals instead of variants. Used by matchLiteral.
 */ export type UnionHandler < T extends string > = { [ P in T ] : ( variant : P ) => any ; } ; /**
 * From a given union type, extract the the variant object's type.
 */ export type VariantsOfUnion < T extends Property < K , string > , K extends string = 'type' > = { [ P in T [ K ] ] : ExtractOfUnion < T , P , K > ; } ; param obj param _type param _typeKey export function cast < O extends Property < K , string > , T extends O [ K ] , K extends string = 'type' > ( obj , _type , _typeKey ? ) { return obj as ExtractOfUnion < O , T , K > ; } param obj param type param typeKey export function narrow < O extends Property < K , string > , T extends O [ K ] , K extends string = 'type' > ( obj , type , typeKey ? ) { const typeString = obj [ typeKey ?? 'type' as K ] ; return typeString === type ? obj as ExtractOfUnion < O , T , K > : undefined ; } export type SumType < T extends VariantModule < K > , K extends string = 'type' > = InternalVariantsOf < T , K > [ keyof T ] ; export type KeyMap < T extends VariantModule < K > , K extends string = 'type' > = { [ Label in keyof T ] : T [ Label ] extends VariantCreator < infer TypeStr , Func , K > ? TypeStr : never ; } ; /**
 * Extract the key literals of a variant.
 */ export type KeysOf < T extends VariantModule < K > , K extends string = 'type' > = KeyMap < T , K > [ keyof T ] ; /**
 * Get the valid options for a variant type's names, plus `undefined`.
 */ export type TypeNames < T extends VariantModule < K > , K extends string = 'type' > = KeysOf < T , K > | undefined ; export type VariantOf < T extends VariantModule < K > , TType = undefined , K extends string = 'type' > = TType extends undefined ? SumType < T , K > : TType extends KeysOf < T , K > ? ExtractOfUnion < SumType < T , K > , TType , K > : SumType < T , K > ; param variantDef export function keys < T extends VariantModule > ( variantDef ) { return strEnum ( outputTypes ( variantDef ) ) as any ; } param v export function keymap < T extends VariantModule < K > , K extends string = 'type' > ( v ) { return Object . keys ( v ) . reduce ( ( acc , key ) => { return { ... acc , [ key ] : v [ key ] . type , } ; } , { } as KeyMap < T , K > ) ; } export type Matrix < T extends VariantModule < K > , K extends string = 'type' > = { [ P in KeysOf < T , K > ] : ExtractOfUnion < SumType < T , K > , P , K > ; } ; /**
 * Splay a list of variant instances into an object.
 */ export type Flags < T extends VariantModule > = Partial < Matrix < T > > ; param flags param typeKey export function flags < T extends Property < K , string > , K extends string = 'type' > ( flags , typeKey ? ) { return flags . reduce ( ( o , v ) => ( { ... o , [ v [ typeKey ?? 'type' ] ] : v , } ) , Object . create ( null ) ) ; }