HTWiki🌔
type-challenges
#Type challenges
My type challenges speed run.
#object
- Partial
- Required
- Readonly
- Record
- Pick
- Omit
- NonNullable
- class
- ConstructorParameters
- InstanceType
- ThisParameterType
- OmitThisParameter
- ThisType
#Pick
type MyPick<T, K extends keyof T> = {
[Key in K]: T[Key];
}
#Readonly
type MyReadonly<T> = {
readonly [K in keyof T]: T[K];
}
#Tuple to Object
type TupleToUnion<T extends unknown[]> = T[number];
type TupleToObject<T extends readonly string[]> = {
[K in TupleToUnion<T>]: K;
}
#Omit
type MyOmit<T, K> = {
[Key in Exclude<keyof T, K>]: T[Key];
}
#Readonly 2
type Merge<T> = {
[K in keyof T]: T[K];
}
type MyReadonly2<T, K extends keyof T = keyof T>
= Merge<Readonly<Pick<T, K>> & Omit<T, K>>;
#Deep Readonly
type DeepReadonly<T> = T extends string|number|boolean|Function ? T : {
readonly [K in keyof T]: DeepReadonly<T[K]>;
}
#Chainable Options
type Merge<T> = {
[K in keyof T]: T[K];
}
type Chainable<T = {}> = {
option<K extends string, V>(key: K, value: V): Chainable<Merge<T & Record<K, V>>>;
get(): T;
}
#Append to object
type Merge<T> = {
[K in keyof T]: T[K];
}
type AppendToObject<T, U extends string, V> = Merge<T & {[K in U]: V}>;
#Merge
type Merge<F, S> = {
[K in keyof F | keyof S]: K extends keyof S
? S[K]
: K extends keyof F ? F[K] : never;
};
#Diff
type Merge<T> = {
[K in keyof T]: T[K];
}
type Diff<T, U> = Merge<Omit<T, keyof U> & Omit<U, keyof T>>;
#ReplaceKeys
type ReplaceKeys<U, T, Y> = {
[K in keyof U]: K extends T
? (K extends keyof Y ? Y[K] : never)
: U[K];
}
#Remove Index Signature
type LiteralOnly<T> = string extends T ? never : number extends T ? never : T;
type RemoveIndexSignature<T> = {
[K in keyof T as LiteralOnly<K>]: T[K];
};
#PickByType
type PickByType<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: T[K];
}
#PartialByKeys
type Merge<T> = {
[K in keyof T]: T[K];
}
type MyPick<T, K> = {
[Key in keyof T as Key extends K ? Key : never]: T[Key];
}
type PartialByKeys<T, K extends string|number|symbol = keyof T>
= Merge<Partial<MyPick<T, K>> & Omit<T, K>>;
#RequiredByKeys
type Merge<T> = {
[K in keyof T]: T[K];
}
type MyPick<T, K> = {
[Key in keyof T as Key extends K ? Key : never]: T[Key];
}
type RequiredByKeys<T, K extends string|number|symbol = keyof T>
= Merge<Required<MyPick<T, K>> & Omit<T, K>>;
#Mutable
type Mutable<T> = T extends Readonly<infer R> ? R : T;
type Mutable<T> = {
-readonly [K in keyof T]: T[K];
}
#OmitByType
type OmitByType<T, U> = {
[K in keyof T as T[K] extends U ? never : K]: T[K];
}
#ObjectEntries
type Dist<T, K extends keyof T = keyof T> = K extends K ? [K, T[K]] : never;
type ObjectEntries<T> = Dist<Required<T>>;
how to invert object's key value?
type Pairs<T, K extends keyof T = keyof T> = K extends K ? [K, T[K]] : never;
type ReversedObject<T extends Record<PropertyKey, PropertyKey>> = {
[P in Pairs<T> as P[0]]: P[1];
}
#InorderTraversal
type InorderTraversal<T> = T extends {val: infer V, left: infer TL, right: infer TR}
? [...InorderTraversal<TL>, V, ...InorderTraversal<TR>]
: [];
#Flip
type Flip<T extends Record<string, string|boolean|number>> = {
[K in keyof T as `${T[K]}`]: K;
}
#MapTypes
type Dist<T, V> = T extends {mapFrom: V} ? T : never;
type MapTypes<T, R extends {mapFrom: unknown, mapTo: unknown}> = {
[K in keyof T]: Dist<R, T[K]> extends never ? T[K] : Dist<R, T[K]>['mapTo']
}
#Simple Vue
type MapReturnType<T> = {
[K in keyof T]: T[K] extends (...args: unknown[]) => infer R ? R : never
}
declare function SimpleVue<D, C, M>(options: {
data?(): D;
computed: C & ThisType<D>;
methods?: M & ThisType<D & M & MapReturnType<C>>;
}): unknown;
??? this type https://www.typescriptlang.org/docs/handbook/utility-types.html#thistypetype
#Get Required
type GetRequired<T> = {
[K in keyof T as Pick<T, K> extends Required<Pick<T, K>> ? K : never]: T[K]
}
#Get Optional
type GetOptional<T> = {
[K in keyof T as Pick<T, K> extends Required<Pick<T, K>> ? never : K]: T[K]
}
#Required Keys
type GetRequired<T> = {
[K in keyof T as Pick<T, K> extends Required<Pick<T, K>> ? K : never]: T[K]
}
type RequiredKeys<T> = keyof GetRequired<T>;
#Optional Keys
type GetOptional<T> = {
[K in keyof T as Pick<T, K> extends Required<Pick<T, K>> ? never : K]: T[K]
}
type OptionalKeys<T> = keyof GetOptional<T>;
#Vue Basic Props
type Constructor<T> = new (...args: any[]) => T;
type ConvertType<T> =
T extends StringConstructor
? string :
T extends BooleanConstructor
? boolean :
T extends NumberConstructor
? number
: T extends Constructor<unknown>
? InstanceType<T>
: T;
type GetType<T> =
T extends {type: infer P}
? P extends unknown[]
? ConvertType<P[number]>
: ConvertType<P>
: T extends Constructor<unknown>
? InstanceType<T>
: T;
type MapProps<T> = {
[K in keyof T]: GetType<T[K]>;
}
type MapReturnType<T> = {
[K in keyof T]: T[K] extends (...args: unknown[]) => infer R ? R : never
}
declare function VueBasicProps<P, D, C, M>(options: {
props: P;
data(this: MapProps<P>): D;
computed: C & ThisType<D&MapProps<P>>;
methods: M & ThisType<MapProps<P> & D & MapReturnType<C> & M>;
}): unknown
#Deep object to unique
declare const KEY: unique symbol;
type DeepObjectToUniq<O, Parent = O, Path extends readonly PropertyKey[] = []> = {
[K in keyof O]: O[K] extends object
? DeepObjectToUniq<O[K], O, [...Path, K]>
: O[K];
} & {
readonly [KEY]?: readonly [Parent, Path];
};
#DeepPick
type Get<T, K> = K extends `${infer P}.${infer Rest}`
? P extends keyof T ? {[Key in P]: Get<T[P], Rest>} : never
: K extends keyof T ? {[Key in K]: T[K]} : never;
type DeepPick<T, P> = UnionToIntersect<Get<T, P>>;
#Pinia
type MapReturnType<T> = {
[K in keyof T]: T[K] extends (...args: unknown[]) => infer R ? R : never
}
declare function defineStore<S, G, A>(store: {
id: string;
state(): S;
getters: G & ThisType<Readonly<S & MapReturnType<G>>>;
actions: A & ThisType<S & Readonly<MapReturnType<G>> & A>;
}): Readonly<S & MapReturnType<G> & A>;
#ClassPublicKeys
type ClassPublicKeys<T> = keyof T;
#IsRequiredKey
type GetRequired<T> = {
[K in keyof T as Pick<T, K> extends Required<Pick<T, K>> ? K : never]: T[K]
}
type RequiredKeys<T> = keyof GetRequired<T>;
// Prevent union type dist
type IsRequiredKey<T, K extends keyof T> = [K] extends [RequiredKeys<T>] ? true : false;
#ObjectFromEntries
type StringOnly<T> = T extends string ? T : never;
type GetN<T, N extends number = 0> = T extends unknown[] ? T[N] : never;
type ObjectFromEntries<T> = {
[P in T as StringOnly<GetN<P>>]: GetN<P, 1>;
}
type ObjectFromEntries<T extends [string, unknown]> = {
[K in T as K[0]]: K[1];
}
similar to ObjectEntries
#Mutable Keys
type GetMutable<T> = {
[K in keyof T as Equal<Readonly<Pick<T, K>>, Pick<T, K>> extends true ? never : K]: T[K];
}
type MutableKeys<T> = keyof GetMutable<T>;
#Get Readonly Keys
type GetReadonly<T> = {
[K in keyof T as Equal<Readonly<Pick<T, K>>, Pick<T, K>> extends true ? K : never]: T[K]
}
type GetReadonlyKeys<T> = keyof GetReadonly<T>;
#array / tuple
#First of Array
type First<T extends unknown[]> = T extends [infer F, ...infer _] ? F : never;
#Length of Tuple
type Length<T extends readonly unknown[]> = T['length'];
#Concat
type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U];
- thinking when object spread will be supported?
#Includes
type Includes<T extends readonly unknown[], U> = T extends [infer F, ...infer Rest]
? Equal<F, U> extends true ? true : Includes<Rest, U>
: false;
#Push
type Push<T extends unknown[], U> = [...T, U];
#Unshift
type Unshift<T extends unknown[], U> = [U, ...T];
#Tuple to Union
type TupleToUnion<T extends unknown[]> = T[number];
#Last of Array
type Last<T extends unknown[]> = T extends [...infer V, infer Last] ? Last : never;
#Pop
type Pop<T> = T extends [...infer Rest, infer _] ? Rest : [];
#Flatten
type Flatten<T> = T extends []
? T
: T extends [infer First, ...infer Rest] ? [...Flatten<First>, ...Flatten<Rest>] : [T];
#AnyOf
type Truthy<T> = Equal<T, {}> extends true ? never : T extends 0|''|false|[] ? never : true;
type AnyOf<T extends readonly unknown[]> = T extends [infer First, ...infer Rest]
? true extends Truthy<First> ? true : AnyOf<Rest>
: false;
type AnyOf<T extends readonly unknown[]> =
T[number] extends 0 | '' | false | [] | Record<string, never> ? false : true;
#MinusOne
type TupleOfLength<T, Result extends unknown[] = []> =
Result['length'] extends T ? Result : TupleOfLength<T, [...Result, 0]>;
type MinusOne<T extends number> =
TupleOfLength<T> extends [infer First, ...infer Rest] ? Rest['length'] : never;
#Shift
type Shift<T> = T extends [infer First, ...infer Rest] ? Rest : T;
#Tuple to Nested Object
type TupleToNestedObject<T, U> = T extends [infer First, ...infer Rest]
? {[K in First as K extends string ? K : never]: TupleToNestedObject<Rest, U>}
: U;
#Reverse
type Reverse<T> = T extends [infer First, ...infer Rest] ? [...Reverse<Rest>, First] : T;
#FlattenDepth
type FlattenDepth<T extends unknown[], N = 1, Acc extends unknown[] = []> =
T extends [infer F, ...infer Rest]
? Acc['length'] extends N
? T
: F extends unknown[]
? [...FlattenDepth<F, N, [...Acc, 0]>, ...FlattenDepth<Rest, N, Acc>]
: [F, ...FlattenDepth<Rest, N, Acc>]
: [];
type FlattenDepth<T, N = 1, Acc extends unknown[] = []> =
T extends [infer F, ...infer Rest]
? Acc['length'] extends N
? T
: [...FlattenDepth<F, N, [...Acc, 0]>, ...FlattenDepth<Rest, N, Acc>]
: T;
#Fibonacci Sequence
// 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...
type Fibonacci<T extends number, Acc extends number[][] = [[], [0]]> = Acc extends [...infer Rest, infer F, infer S]
? Rest['length'] extends T
? F extends number [] ? F['length'] : never
: F extends number[] ? S extends number[] ? Fibonacci<T, [...Acc, [...F, ...S]]> : never : never
: never;
#Greater Than
type TupleOfLength<T, Result extends unknown[] = []> =
Result['length'] extends T ? Result : TupleOfLength<T, [...Result, 0]>;
type MinusOne<T extends number> =
TupleOfLength<T> extends [infer First, ...infer Rest] ? Rest['length'] : never;
type GreaterThan<T extends number, U extends number> =
T extends 0 ? false : U extends 0 ? true : GreaterThan<MinusOne<T>, MinusOne<U>>;
#Zip
type Zip<T extends unknown[], U extends unknown[]> = T extends [infer TF, ...infer TRest]
? (U extends [infer UF, ...infer URest] ? [[TF, UF], ...Zip<TRest, URest>] : [])
: [];
type Zip<T extends unknown[], U extends unknown[]> =
[T, U] extends [[infer L, ...infer RestT], [infer R, ...infer RestU]]
? [[L, R], ...Zip<RestT, RestU>]
: [];
#Chunk
type Chunk<T extends unknown[], L, Acc extends unknown[] = []> = Acc['length'] extends L
? [Acc, ...Chunk<T, L, []>]
: T extends [infer First, ...infer Rest]
? Chunk<Rest, L, [...Acc, First]>
: Acc extends [] ? [] : [Acc];
#Fill
type Fill<
T extends unknown[],
N,
Start extends number = 0,
End extends number = T['length'],
Result extends unknown[] = [],
Flag = false
> = Flag extends true
? Result['length'] extends End
? [...Result, ...T]
: T extends [infer F, ...infer Rest]
? Fill<Rest, N, Start, End, [...Result, N], true>
: Result
: Result['length'] extends Start
? Fill<T, N, Start, End, Result, true>
: T extends [infer F, ...infer Rest]
? Fill<Rest, N, Start, End, [...Result, F], false>
: Result;
#Without
type ToUnion<T> = T extends unknown[] ? T[number] : T;
type Without<T, U> = T extends [infer F, ...infer Rest]
? F extends ToUnion<U> ? Without<Rest, U> : [F, ...Without<Rest, U>]
: T;
#IndexOf
type IndexOf<T, U> = T extends [...infer Rest, infer L]
? L extends U
? IndexOf<Rest, U> extends -1
? Rest['length']
: IndexOf<Rest, U>
: IndexOf<Rest, U>
: -1;
type IndexOf<T, U, Acc extends unknown[] = []> = T extends [infer F, ...infer Rest]
? F extends U ? Acc['length'] : IndexOf<Rest, U, [...Acc, F]>
: -1;
#LastIndexOf
type LastIndexOf<T, U> = T extends [...infer Rest, infer L]
? L extends U ? Rest['length'] : LastIndexOf<Rest, U>
: -1;
#Unique
type Unique<T, Seen = never> = T extends [infer F, ...infer Rest]
? F extends Seen ? Unique<Rest, Seen> : [F, ...Unique<Rest, Seen|F>]
: [];
#Promise.all
type UnPromise<T> = T extends Promise<infer P> ? P : T;
type UnPromiseList<T> = T extends [infer F, ...infer Rest] ? [UnPromise<F>, ...UnPromiseList<Rest>] : [];
declare function PromiseAll<T extends unknown[]>(values: readonly [...T]): Promise<UnPromiseList<T>>;
type UnPromise<T> = {
[K in keyof T]: T[K] extends Promise<infer V> ? V : T[K];
}
declare function PromiseAll<T extends unknown[]>(values: readonly [...T]): Promise<UnPromise<T>>;
#Tuple Filter
type FilterOut<T extends unknown[], F> = T extends [infer First, ...infer Rest]
? [First] extends [F] ? FilterOut<Rest, F> : [First, ...FilterOut<Rest, F>]
: [];
#Tuple to Enum Object
type Cap<T> = T extends string ? Capitalize<T> : never;
type GetNPair<T, Acc extends unknown[] = []>
= T extends readonly [infer F, ...infer Rest]
? [[Cap<F>, Acc['length']], ...GetNPair<Rest, [...Acc, unknown]>]
: [];
type GetKPair<T>
= T extends readonly [infer F, ...infer Rest]
? [[Cap<F>, F], ...GetKPair<Rest>]
: [];
type GetPair<T, N> = N extends true ? GetNPair<T> : GetKPair<T>;
type StringOnly<T> = T extends string ? T : never;
type GetN<T, N extends number = 0> = T extends unknown[] ? T[N] : never;
type ObjectFromEntries<T> = {
[P in T as StringOnly<GetN<P>>]: GetN<P, 1>;
}
type Enum<T extends readonly string[], N extends boolean = false>
= Readonly<ObjectFromEntries<GetPair<T, N>[number]>>;
#Slice
similar to Fill
type SlicePos<
T extends unknown[],
Start extends number = 0,
End extends number = T['length'],
Acc extends unknown[] = [],
Flag = false
> = Flag extends true
? Acc['length'] extends End
? []
: T extends [infer F, ...infer Rest]
? [F, ...SlicePos<Rest, Start, End, [...Acc, F], true>]
: []
: Acc['length'] extends Start
? SlicePos<T, Start, End, Acc, true>
: T extends [infer F, ...infer Rest]
? SlicePos<Rest, Start, End, [...Acc, F], false>
: [];
type Slice<
T extends unknown[],
Start extends number = 0,
End extends number = T['length'],
> = SlicePos<T, MakePos<Start, T['length']>, MakePos<End, T['length']>>
#Integers Comparator
enum Comparison {
Greater,
Equal,
Lower,
}
type IsNeg<T extends number> = `${T}` extends `-${infer _}` ? true : false;
type Abs<T extends number> = `${T}` extends `-${infer V}` ? V : `${T}`;
type Comp<A extends string, B extends string, Acc extends unknown[] = []> = A extends B
? Comparison.Equal
: `${Acc['length']}` extends A
? Comparison.Lower
: `${Acc['length']}` extends B
? Comparison.Greater
: Comp<A, B, [...Acc, unknown]>;
type Comparator<A extends number, B extends number> = IsNeg<A> extends true
? IsNeg<B> extends true
? Comp<Abs<B>, Abs<A>>
: Comparison.Lower
: IsNeg<B> extends true
? Comparison.Greater
: Comp<`${A}`, `${B}`>;
#function
- Parameters
- ReturnType
#Parameters
type MyParameters<T> = T extends (...args: infer Args) => unknown ? Args : never;
#Get Return Type
type MyReturnType<T> = T extends (...args: infer Args) => infer R ? R : never;
#Append Argument
type AppendArgument<Fn, A> = Fn extends (...args: infer Args) => infer R
? (...args: [...Args, A]) => R
: Fn;
#Flip Arguments
type Reverse<T> = T extends [infer First, ...infer Rest] ? [...Reverse<Rest>, First] : T;
type FlipArguments<T> = T extends (...args: infer Args) => infer R ? (...args: Reverse<Args>) => R : T;
type FlipArguments<T extends (...args: any) => any> = (...args: Reverse<Parameters<T>>) => ReturnType<T>;
#Currying 1
type Curry<T> = T extends (...args: infer Args) => infer R
? Args extends [infer F, infer S, ...infer Rest]
? (x: F) => Curry<(...args: [S, ...Rest]) => R>
: T
: never;
declare function Currying<T>(fn: T): Curry<T>;
#Currying 2
type ArgsUnion<T extends unknown[]> = T extends [infer F, ...infer Rest]
? [F] | [F, ...ArgsUnion<Rest>]
: [];
type Drop<T, N extends number, Acc extends unknown[] = []>
= Acc['length'] extends N ? T : T extends [infer _, ...infer Rest] ? Drop<Rest, N, [...Acc, unknown]> : never;
type Curried<T> = T extends (...args: infer Args) => infer R
? <A extends ArgsUnion<Args>>(...args: A) => A extends Args
? R
: Curried<(...args: Drop<Args, A['length']>) => R>
: never;
declare function DynamicParamsCurrying<T>(fn: T): Curried<T>;
#string
- Uppercase
- Lowercase
- Capitalize
- Uncapitalize
#Typed Get
type Get<T, K> = K extends `${infer P}.${infer Rest}`
? Get<Get<T, P>, Rest>
: K extends keyof T ? T[K] : never;
#Trim Left
type Space = ' '|'\n'|'\t';
type TrimLeft<S extends string> = S extends `${Space}${infer Rest}` ? TrimLeft<Rest> : S;
#Trim
type Space = ' '|'\n'|'\t';
type TrimLeft<S extends string> = S extends `${Space}${infer Rest}` ? TrimLeft<Rest> : S;
type TrimRight<S extends string> = S extends `${infer Rest}${Space}` ? TrimRight<Rest> : S;
type Trim<S extends string> = TrimRight<TrimLeft<S>>;
#Capitalize
type Capitalize<S extends string> = S extends `${infer C}${infer Rest}` ? `${Uppercase<C>}${Rest}` : S;
#Replace
type Replace<S extends string, From extends string, To extends string> = From extends ''
? S
: S extends `${infer C}${From}${infer Rest}` ? `${C}${To}${Rest}` : S;
#ReplaceAll
type ReplaceAll<S extends string, From extends string, To extends string> = From extends ''
? S
: S extends `${infer C}${From}${infer Rest}` ? `${C}${To}${ReplaceAll<Rest, From, To>}` : S;
#Length of String
type LengthOfString<S extends string, T extends unknown[] = []> =
S extends `${infer C}${infer Rest}` ? LengthOfString<Rest, [...T, C]> : T['length'];
#Absolute
type Absolute<T extends number | string | bigint> = `${T}` extends `-${infer V}` ? V : `${T}`;
#String to Union
type StringToUnion<T extends string> =
T extends `${infer C}${infer Rest}` ? C|StringToUnion<Rest> : never;
#CamelCase
type CamelCase<S> = S extends `${infer First}-${infer C}${infer Rest}`
? Uppercase<C> extends C
? `${First}-${CamelCase<`${C}${Rest}`>}`
: `${First}${Uppercase<C>}${CamelCase<Rest>}`
: S;
#KebabCase
type KebabCase<S, D extends string = ''> = S extends `${infer C}${infer Rest}`
? C extends Lowercase<C>
? `${C}${KebabCase<Rest, '-'>}`
: `${D}${Lowercase<C>}${KebabCase<Rest, '-'>}`
: S;
#Percentage Parser
type MP = '+'|'-';
type P = '%';
type Sign<T> = T extends `${infer C}${infer Rest}`? C extends MP ? C : '' : '';
type Percentage<T> = T extends `${infer Rest}${P}`? P : '';
type DropSign<T> = T extends `${MP}${infer Rest}` ? Rest : T;
type DropPercentage<T> = T extends `${infer Rest}%` ? Rest : T;
type PercentageParser<T> = [Sign<T>, DropPercentage<DropSign<T>>, Percentage<T>];
#Drop Char
type DropChar<S, C extends string> =
S extends `${infer First}${C}${infer Last}` ? DropChar<`${First}${Last}`, C> : S;
#StartsWith
type StartsWith<T extends string, U extends string> = T extends `${U}${infer Rest}`? true : false;
#EndsWith
type EndsWith<T extends string, U extends string> = T extends `${infer Rest}${U}`? true : false;
#BEM style string
type Dist<T extends string, U extends string, D extends string> =
[U] extends [never] ? T : U extends U ? `${T}${D}${U}` : never;
type BEM<B extends string, E extends string[], M extends string[]> =
Dist<Dist<B, E[number], '__'>, M[number], '--'>;
#AllCombinations
type Permutation<T extends string, U extends string = T> = Equal<T, never> extends true
? ''
: (T extends U ? `${T}${Permutation<Exclude<U, T>>}` : never);
type AllCombinations<S, Acc extends string = never> = S extends `${infer F}${infer Rest}`
? Permutation<Acc|F> | AllCombinations<Rest, Acc> | AllCombinations<Rest, Acc|F>
: '';
???
type AllCombinations<S, Acc extends string = ''> = S extends `${infer F}${infer Rest}`
? `${F}${AllCombinations<`${Acc}${Rest}`>}` | AllCombinations<Rest, `${Acc}${F}`>
: '';
literal template string matching behavior:
// Match all
type T = '' extends `${infer R}` ? R : never; // ''
type T = '1' extends `${infer R}` ? R : never; // '1'
type T = '12' extends `${infer R}` ? R : never; // '12'
// Match first char
type T = '' extends `${infer R}${infer Rest}` ? R : never; // never
type T = '1' extends `${infer R}${infer Rest}` ? R : never; // '1'
type T = '12' extends `${infer R}${infer Rest}` ? R : never; // '1'
#Trim Right
type Space = ' '|'\n'|'\t';
type TrimRight<S extends string> = S extends `${infer Rest}${Space}` ? TrimRight<Rest> : S;
#Trunc
type Trunc<T extends number|string> = `${T}` extends `${infer Rest}.${infer _}` ? Rest : `${T}`;
#Join
type Join<T, U extends string, D extends string = ''> = T extends [infer F, ...infer Rest]
? F extends string ? `${D}${F}${Join<Rest, U, U>}` : never
: '';
#Capitalize Words
type CapitalizeWords<S extends string, Flag = true> =
S extends `${infer F}${infer Rest}`
? Flag extends true
? `${Uppercase<F>}${CapitalizeWords<Rest, false>}`
: `${F}${CapitalizeWords<Rest, F extends ' '|','|'.' ? true : false>}`
: '';
type FirstDivider<S> = S extends `${infer C}${infer Rest}` ?
C extends ','|'.'|' ' ? C : FirstDivider<Rest>
: never;
type CapitalizeWords<S extends string> =
S extends `${infer W}${FirstDivider<S>}${infer Rest}` ?
`${Capitalize<W>}${FirstDivider<S>}${CapitalizeWords<Rest>}`
: Capitalize<S>;
#CamelCase
type CamelCase2<S, D extends string> = S extends `${infer First}${D}${infer C}${infer Rest}`
? Uppercase<C> extends C
? `${First}${D}${CamelCase<`${C}${Rest}`>}`
: `${First}${Uppercase<C>}${CamelCase<Rest>}`
: S;
type CamelCase<S extends string> = CamelCase2<Lowercase<S>, '_'>;
#C-printf Parser
type ParsePrintFormat<S, Prev = ''> = S extends `${infer F}${infer Rest}`
? Prev extends '%'
? F extends keyof ControlsMap
? [ControlsMap[F], ...ParsePrintFormat<Rest, ''>]
: ParsePrintFormat<Rest, ''>
: ParsePrintFormat<Rest, F>
: [];
#String to Number
type ToNumber<S extends string, Acc extends unknown[] = []> =
S extends `${Acc['length']}` ? Acc['length'] : ToNumber<S, [...Acc, 0]>;
#printf
type CMap = {
's': string;
'd': number;
}
type Format<T extends string, Prev = ''> = T extends `${infer F}${infer Rest}`
? Prev extends '%'
? F extends keyof CMap
? (x: CMap[F]) => Format<Rest, ''>
: Format<Rest, ''>
: Format<Rest, F>
: string;
#Length of String 2
TODO:
- trick
#String Join
type Join<T, U extends string, D extends string = ''> = T extends [infer F, ...infer Rest]
? F extends string ? `${D}${F}${Join<Rest, U, U>}` : never
: '';
declare function join<D extends string>(delimiter: D): <T extends string[]>(...parts: T) => Join<T, D>;
#Camelize
type CamelCase2<S, D extends string> = S extends `${infer First}${D}${infer C}${infer Rest}`
? Uppercase<C> extends C
? `${First}${D}${CamelCase<`${C}${Rest}`>}`
: `${First}${Uppercase<C>}${CamelCase<Rest>}`
: S;
type CamelCase<S extends string> = CamelCase2<Lowercase<S>, '_'>;
type Camelize<T> = T extends unknown[]
? T extends [infer F, ...infer Rest]
? [Camelize<F>, ...Camelize<Rest>]
: []
: T extends string
? CamelCase<T>
: {
[K in keyof T as Camelize<K>]: Camelize<T[K]>;
};
#Drop String
type StringToUnion<T extends string> =
T extends `${infer C}${infer Rest}` ? C|StringToUnion<Rest> : never;
type DropString<S, R extends string > = S extends `${infer C}${infer Rest}`
? `${C extends StringToUnion<R> ? '' : C}${DropString<Rest, R>}`
: S;
#Split
type Split<S, SEP extends string> = string extends S
? string[]
: S extends `${infer F}${SEP}${infer Rest}`
? [F, ...Split<Rest, SEP>]
: S extends SEP ? [] : [S];
#IsPalindrome
type IsPalindrome<T extends number|string>
= `${T}` extends `${infer F}${infer _}`
? `${T}` extends F
? true
: `${T}` extends `${F}${infer Rest}${F}` ? IsPalindrome<Rest> : false
: false;
#Query String Parser
type Split<S, SEP extends string> = string extends S
? string[]
: S extends `${infer F}${SEP}${infer Rest}`
? [F, ...Split<Rest, SEP>]
: S extends SEP ? [] : [S];
type StringOnly<T> = T extends string ? T : never;
type ToObj<T extends unknown[]> =
T extends [infer F, infer L]
? Record<StringOnly<F>, L>
: T[0] extends '' ? {} : Record<StringOnly<T[0]>, true>;
type MapKV<T> = T extends [infer F, ...infer Rest] ? [ToObj<Split<F, '='>>, ...MapKV<Rest>] : [];
type ToPair<T> = MapKV<Split<T, '&'>>;
type Merge<O, T> = {
[K in keyof T | keyof O]: K extends keyof T
? K extends keyof O
? O[K] extends T[K] ? T[K] : [O[K], T[K]]
: T[K]
: K extends keyof O ? O[K] : never;
}
type MergeObj<T> = T extends [infer F, ...infer Rest] ? Merge<F, MergeObj<Rest>> : {};
type ParseQueryString<T> = MergeObj<ToPair<T>>;
#others, union, intersection etc
- Exclude
- Extract
#Exclude
type MyExclude<T, U> = T extends U ? never : T;
#Awaited
type MyAwaited<T> = T extends Promise<infer V> ? MyAwaited<V> : T;
- conditional
- infer
#If
type If<C, T, F> = C extends true ? T : F;
#Type Lookup
type LookUp<U, T> = U extends {type: T} ? U : never;
#Permutation
type Permutation<T, U = T> = Equal<T, never> extends true
? []
: (T extends U ? [T, ...Permutation<Exclude<U, T>>] : never);
type Permutation<T, U = T> = [T] extends [never]
? []
: (T extends U ? [T, ...Permutation<Exclude<U, T>>] : never);
#IsNever
type IsNever<T> = Equal<never, T>;
type IsNever<T> = [T] extends [never] ? true : false;
#IsUnion
type NotEqual<T, U> = Equal<T, U> extends true ? false : true;
type Dist<T> = T extends T ? [T] : never;
type IsUnion<T> = NotEqual<[T], Dist<T>>;
type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true
type IsUnion<T> = UnionToTuple<T>['length'] extends 1 ? false : true;
#IsTuple
type IsTuple<T> = T extends readonly [infer T, ...infer Rest] ? IsTuple<Rest> : Equal<T, []>;
type IsTuple<T> = T extends readonly unknown[]
? number extends T['length'] ? false : true : false;
IsTuple<never>
never has some strange behavior.
[T] extends [never]
#Union to Intersection
type Dist<T> = T extends T ? (x: T) => void : never;
type UnionToIntersection<U> =
Dist<U> extends (x: infer X) => void ? X : never;
// X|Y
// Dist<X|Y> = (x: X) => void | (x: Y) => void;
// UnionToIntersection<X|Y> = X&Y
- naked vs boxed type
- covariance vs contravariance
#IsAny
type IsAny<T> = Equal<any, T>;
#Union to Tuple
type Dist<T> = T extends T ? (x: T) => void : never;
type UnionToIntersection<U> =
Dist<U> extends (x: infer X) => void ? X : never;
// X|Y
// Dist<X|Y> = (x: X) => void | (x: Y) => void;
// UnionToIntersection<X|Y> = X&Y
type LastInUnion<U> =
UnionToIntersection<Dist<U>> extends (x: infer L) => void
? L
: never;
// Dist<X|Y> = (x: X) => void | (x: Y) => void;
// UnionToIntersection<Dist<X|Y>> = (x: X) => void & (x: Y) => void
// LastInUnion<X|Y> = ?
type UnionToTuple<U, Last = LastInUnion<U>> = [U] extends [never]
? []
: [...UnionToTuple<Exclude<U, Last>>, Last];
#Intersection
type ToUnion<T> = T extends unknown[] ? T[number] : T;
type Intersection<T> = T extends [infer F]
? ToUnion<F>
: T extends [infer F, ...infer Rest]
? ToUnion<F> & Intersection<Rest>
: never;
#Sum
type ToString<T> = T extends number ? `${T}` : T;
type TupleOfLength<T extends string|number|bigint, Result extends unknown[] = []> =
`${Result['length']}` extends `${T}` ? Result : TupleOfLength<T, [...Result, 0]>;
type Sum<A extends string | number | bigint, B extends string | number | bigint> = ToString<[...TupleOfLength<A>, ...TupleOfLength<B>]['length']>;
#TODO:
#Multiply
TODO:
repeat by sum
#Tag
TODO:
#Inclusive Range
TODO:
#Sort
TODO:
#DistributeUnions
TODO:
#Assert Array Index
TODO: