import { DeepPartial } from "./objects"; import { Join } from "./strings"; export type IsAny = 0 extends (1 & T) ? true : false; export type IsNever = [T] extends [never] ? true : false; export type IsNullish = [T] extends [null | undefined] ? true : false; export type IsUnion = IsNever extends true ? false : IsAny extends true ? false : T extends U // distributive conditional https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types ? /* if the *whole* original type (`U`) still fits inside the current variant, then `T` wasn’t a union */ ([U] extends [T] ? false : true) : never; export type NullishCoalesce = T extends null | undefined ? U : T; export type LastUnionElement = UnionToIntersection 0 : never> extends (x: infer L) => 0 ? L & U : never; type primitive = string | number | boolean | bigint | symbol | null | undefined; /** * Makes a type prettier by recursively expanding all object types. For example, `Omit<{ a: 1 }, "a">` becomes just `{}`. */ export type Expand = T extends (...args: infer A) => infer R ? ((...args: A) => R) extends T ? (...args: Expand) => Expand : ((...args: Expand) => Expand) & { [K in keyof T]: Expand } : T extends object ? T extends primitive ? T : T extends infer O ? { [K in keyof O]: Expand } : never : T; /** * Removes all optional undefined/never keys from an object. */ export type DeepRemoveOptionalUndefined = T extends object ? { [K in keyof T]: DeepRemoveOptionalUndefined } : T; // why this works: https://stackoverflow.com/a/50375286 export type UnionToIntersection = (U extends any ? (x: U) => void : never) extends ((x: infer I) => void) ? I : never type _UnionToTupleInner = UnionToTuple, [...R, Last]> export type UnionToTuple = [U] extends [never] ? R : _UnionToTupleInner>; export type CollapseObjectUnion = { [K in AllUnionKeys]?: T extends Record ? V : never; }; typeAssertIs, { a?: string, b?: number }>()(); typeAssertIs, { a?: string | number }>()(); export type IntersectAll = UnionToIntersection; export type OptionalKeys = { [K in keyof T]: {} extends Pick ? K : never; }[keyof T]; export type RequiredKeys = { [K in keyof T]: {} extends Pick ? never : K; }[keyof T]; /** * Returns a type whose keys are the intersection of the keys of T and U, deeply. */ export type KeyIntersect = & { [K in keyof T & keyof U]?: T[K] & U[K] } & { [K in RequiredKeys & keyof U]: T[K] & U[K] } & { [K in RequiredKeys & keyof T]: U[K] & T[K] } /** * Returns ALL keys of all union elements. */ export type AllUnionKeys = T extends T ? keyof T : never; typeAssertIs, "a" | "b">()(); export type SubtractType = T extends object ? { [K in keyof T]: K extends keyof U ? SubtractType : T[K] } : (T extends U ? never : T); // note: this only works due to the distributive property of conditional types https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types export type XOR = T extends readonly [infer A, infer B, ...infer Rest] ? Rest extends [] ? (A & { [K in keyof B]?: never }) | (B & { [K in keyof A]?: never }) : XOR<[(A & { [K in keyof B]?: never }) | (B & { [K in keyof A]?: never }), ...Rest]> : T[0]; type _AntiIntersectInner = T extends object ? ( & Omit & { [K in keyof Pick]: PseudoAntiIntersect } & { [K in keyof Pick]?: PseudoAntiIntersect } ) : U; /** * Returns a type R such that T & R = U. */ export type AntiIntersect = U extends T ? _AntiIntersectInner : "Cannot anti-intersect a type with a type that is not a subtype of it"; // NOTE: This type is mostly untested — not sure how well it works on the edge cases export type PseudoAntiIntersect = _AntiIntersectInner; /** * A variation of TypeScript's conditionals with slightly different semantics. It is the perfect type for cases where: * * - If all possible values are contained in `Extends`, then it will be mapped to `Then`. * - If all possible values are not contained in `Extends`, then it will be mapped to `Otherwise`. * - If some possible values are contained in `Extends` and some are not, then it will be mapped to `Then | Otherwise`. * * This is different from TypeScript's built-in conditional types (`Value extends Extends ? Then : Otherwise`), which * returns `Otherwise` for the third case (causing unsoundness in many real-world cases). */ export type IfAndOnlyIf = | (Value extends Extends ? never : Otherwise) | (Value & Extends extends never ? never : Then); /** * Can be used to prettify a type in the IDE; for example, some complicated intersected types can be flattened into a single type. */ export type PrettifyType = T extends object ? { [K in keyof T]: T[K] } & {} : T; type _ToStringAndJoin = T extends [infer U, ...infer Rest extends any[]] ? `${TypeToString}${Rest extends [any, ...any[]] ? `${Separator}${_ToStringAndJoin}` : ""}` : ""; type _TypeToStringInner = IsAny extends true ? "any" : IsNever extends true ? "never" : IsUnion extends true ? _ToStringAndJoin, " | "> : [T] extends [number] ? (number extends T ? "number" : `${T}`) : [T] extends [boolean] ? `${T}` : [T] extends [undefined] ? "undefined" : [T] extends [null] ? "null" : [T] extends [string] ? (string extends T ? "string" : `'${T}'`) : [T] extends [[]] ? "[]" : [T] extends [[any, ...any[]]] ? `[${_ToStringAndJoin}]` : [T] extends [(infer E)[]] ? `${TypeToString}[]` : [T] extends [Function] ? "function" : [T] extends [symbol] ? `symbol(${T['description']})` : [T] extends [object] ? `{ ${Join}: ${TypeToString}` }[keyof T]>, ", ">} }` : "" export type TypeToString = _TypeToStringInner extends `${infer S}` ? S : never; /** * Can be used to create assertions on types. For example, if passed any T other than `true`, the following will * show a type error: * * ```ts * typeAssert()(); // the second pair of braces is important! * ``` */ export function typeAssert(): ( IsAny extends true ? TypeAssertionError<`Type assertion failed. Expected true, but got any.`> : IsNever extends true ? TypeAssertionError<`Type assertion failed. Expected true, but got never.`> : T extends true ? (() => undefined) : TypeAssertionError<`Type assertion failed. Expected true, but got: ${TypeToString}`> ) { return (() => undefined) as any; } type TypeAssertionError = & [T] & /* this promise makes sure that if we accidentally forget the second pair of braces, eslint will complain (if we have no-floating-promises enabled) */ Promise; typeAssertExtends>, () => undefined>()(); typeAssertExtends>, TypeAssertionError<`Type assertion failed. Expected true, but got: false`>>()(); typeAssertExtends>, TypeAssertionError<`Type assertion failed. Expected true, but got never.`>>()(); typeAssertExtends>, TypeAssertionError<`Type assertion failed. Expected true, but got any.`>>()(); /** * Functionally equivalent to `typeAssert()()`, but with better error messages. */ export function typeAssertExtends(): ( [T] extends [S] ? (() => undefined) : TypeAssertionError<`Type assertion failed. Expected ${TypeToString} to extend ${TypeToString}`> ) { return (() => undefined) as any; } typeAssertExtends>, () => undefined>()(); typeAssertExtends>, () => undefined>()(); typeAssertExtends>, () => undefined>()(); typeAssertExtends>, () => undefined>()(); typeAssertExtends>, () => undefined>()(); typeAssertExtends>, () => undefined>()(); typeAssertExtends>, () => undefined>()(); typeAssertExtends>, ["Type assertion failed. Expected { 'a': number } to extend { 'a': 1 }"]>()(); typeAssertExtends>, ["Type assertion failed. Expected any to extend never"]>()(); typeAssertExtends>, ["Type assertion failed. Expected false to extend true"]>()(); typeAssertExtends>, ["Type assertion failed. Expected false to extend never"]>()(); export function typeAssertIs(): ( IsAny extends true ? (IsAny extends true ? (() => undefined) : TypeAssertionError<`Type assertion failed. Expected ${TypeToString} to be ${TypeToString}`>) : IsAny extends true ? TypeAssertionError<`Type assertion failed. Expected ${TypeToString} to be ${TypeToString}`> : [T] extends [U] ? ([U] extends [T] ? (() => undefined) : TypeAssertionError<`Type assertion failed. Expected ${TypeToString} to be ${TypeToString}`>) : TypeAssertionError<`Type assertion failed. Expected ${TypeToString} to be ${TypeToString}`> ) { return (() => undefined) as any; } typeAssertExtends>, () => undefined>()(); typeAssertExtends>, () => undefined>()(); typeAssertExtends, {a?: 1}>>, () => undefined>()(); typeAssertExtends>, () => undefined>()(); typeAssertExtends>, () => undefined>()(); typeAssertExtends>, ["Type assertion failed. Expected 1 to be any"]>()(); typeAssertExtends>, ["Type assertion failed. Expected any to be 1"]>()(); typeAssertExtends>, ["Type assertion failed. Expected false to be true"]>()(); typeAssertExtends>, ["Type assertion failed. Expected { 'a': number } to be { 'a': 1 }"]>()(); typeAssertExtends>, ["Type assertion failed. Expected any to be never"]>()(); typeAssertExtends>, ["Type assertion failed. Expected false to be true"]>()(); typeAssertExtends>, ["Type assertion failed. Expected false to be never"]>()();