// when T is any|unknown, Y is returned, otherwise N type IsAnyUnknown = unknown extends T ? Y : N; // when T is never, Y is returned, otherwise N type IsNever = [T] extends [never] ? Y : N; // when T is a tuple, Y is returned, otherwise N // valid tuples = [string], [string, boolean], // invalid tuples = [], string[], (string | number)[] type IsTuple = T extends [any, ...any[]] ? Y : N; // empty object type EmptyObject = { [key: string]: never }; // is a type an empty object? type IsEmpty = T extends EmptyObject ? Y : N; // returns the value type for T[K] without having to do "K extends keyof T ? T[K] : never" type Lookup = T extends any ? K extends keyof T ? T[K] : N : N; // sometimes you have a type with never values, this removes those keys from T type StripNever = Pick }[keyof T]>; // sometimes something is an expected type, but TypeScript has problem recognizing it. // This can ensure the expected type is being used. type Cast = T extends AS ? T : never; // Returns the result of { ...A, ...B } type MergeObjects = { [K in keyof B]: undefined extends B[K] ? K extends keyof A ? Exclude | A[K] : B[K] : B[K] } & { [K in keyof A]: K extends keyof B ? undefined extends B[K] ? Exclude | A[K] : B[K] : A[K] }; // Which keys in T are undefined type UndefinedKeys = { [P in keyof T]-?: undefined extends T[P] ? P : never }[keyof T]; // When a type is really deep and has retained an unnessecary amount of type information, // this flattens it to a single array/object/value. type Simplify = T extends (object | any[]) ? { [K in keyof T]: T[K] } : T; // Converts { x: string | undefined } to { x?: string | undefined } type UndefinedToOptional = Simplify< Pick>> & Partial>> >; // Converts { x: string } | { y: number } to { x: string, y: number } type UnionToIntersection = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never; // Converts string | number | boolean to [string, number, boolean] type UnionToTuple = ( ( ( T extends any ? (t: T) => T : never ) extends infer U ? (U extends any ? (u: U) => any : never ) extends (v: infer V) => any ? V : never : never ) extends (_: any) => infer W ? [...UnionToTuple>, W] : [] ); // Converts (string | number)[] to [string, number] type ArrayToTuple = T extends Array ? UnionToTuple : T ; // Merges objects and changes undefined to optional if any exist. type AppendObjects = UndefinedToOptional> ; // AppendTuples<[string, boolean], [string, number]> = [string, boolean, string, number] type AppendTuples = Simplify<[...A, ...B]> ; // JSON type JsonScalar = number | string | boolean | null; type JsonObject = { [key: string]: JsonScalar | JsonObject | JsonArray; }; type JsonArray = Array; type Json = JsonScalar | JsonObject | JsonArray; // ObjectKeys<{ x: string, y: number }> = ["x", "y"] type ObjectKeys = UnionToTuple extends Array ? UnionToTuple : never; // The properties of T should be Partialed type PartialChildren = { [K in keyof T]: Partial }; // JoinTuples<[[string], [], [boolean, number], [string, Date]]> = [string, boolean, number, string, Date] type JoinTuples = T extends [infer A] ? ToTuple : T extends [infer B, ...infer C] ? [...ToTuple, ...JoinTuples] : []; // Converts anything to a tuple or array, unless it aleady is. type ToTuple = T extends any[] ? T : [T]; // A extends B is not good enough sometimes, especially when never is involved. // Also order matters when doing extends, so this does both directions. type Extends = [A] extends [B] ? [B] extends [A] ? T : F : F; /** * forbids manipulation for the wrapped type (recursive) * but i guess its not hard enough against typecasting * * * credits and thanks to https://templecoding.com/blog/real-immutable-types-with-typescript * --> original * fails to be compatible to "Date"-Objects * * const immDate: Immutable = new Date(1); * const date: Date = immDate; // Error * * --> workaround * adding "T &" {... */ export type Immutable = T & { readonly [K in keyof T]: Immutable }; // Testing return types. // If the input doesn't match T there will be a TS error. // // expectType('hello') function expectType(type: Expected) {} // Testing types against each other (used to unit test the above types) // If the types don't match there will be a TS error. // // expectTypeMatch<["x", "y"], ObjectKeys<{ x: string, y: number }>>(true); // // If you want to test they are NOT equivalent, pass false to function. function expectTypeMatch(truthy: Extends) {}