UNPKG

hd-utils

Version:

A handy utils for modern JS developers

358 lines (357 loc) 12.1 kB
export type Key = string | number; export type Keys<T> = keyof T; export type Values<T> = T[Keys<T>]; export type ExcludeFromArray<T extends any[], ToExclude> = Exclude<T[number], ToExclude>[]; export type NoopFn = (..._: any[]) => void; export declare type FunctionReturningPromise = (...args: any[]) => Promise<any>; export type PredicateFunc<T> = (key: string | symbol, value: string | Values<T>, object: {} | object) => boolean; export type KeysArr = (string | symbol)[]; /** * Recursively unwraps the "awaited type" of a type. Non-promise "thenables" should resolve to `never`. This emulates the behavior of `await`. */ export type PAwaited<T> = T extends null | undefined ? T : T extends object & { then(onfulfilled: infer F, ...args: infer _): any; } ? F extends (value: infer V, ...args: infer _) => any ? Awaited<V> : never : T; export type Nullable<T> = T | null; export type NullableOrUndefined<T> = T | null | undefined; /** * ReadonlyKeys * @desc Get union type of keys that are readonly in object type `T` * Credit: Matt McCutchen * https://stackoverflow.com/questions/52443276/how-to-exclude-getter-only-properties-from-type-in-typescript * @example * type Props = { readonly foo: string; bar: number }; * * // Expect: "foo" * type Keys = ReadonlyKeys<Props>; */ export type ReadonlyKeys<T extends object> = { [P in keyof T]-?: IfEquals<{ [Q in P]: T[P]; }, { -readonly [Q in P]: T[P]; }, never, P>; }[keyof T]; /** * @description will check if X, Y are equal */ export type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? A : B; /** * Falsy * @desc Type representing falsy values in TypeScript: `false | "" | 0 | null | undefined` * @example * type Various = 'a' | 'b' | undefined | false; * * // Expect: "a" | "b" * Exclude<Various, Falsy>; */ export type Falsy = false | '' | 0 | null | undefined; /** * Primitive * @desc Type representing [`Primitive`](https://developer.mozilla.org/en-US/docs/Glossary/Primitive) types in TypeScript: `string | number | bigint | boolean | symbol | null | undefined` * @example * type Various = number | string | object; * * // Expect: object * type Cleaned = Exclude<Various, Primitive> */ export type Primitive = string | number | bigint | boolean | symbol | null | undefined; /** * NonUndefined * @desc Exclude undefined from set `A` * @example * // Expect: "string | null" * SymmetricDifference<string | null | undefined>; */ export type NonUndefined<A> = A extends undefined ? never : A; /** * FunctionKeys * @desc Get union type of keys that are functions in object type `T` * @example * type MixedProps = {name: string; setName: (name: string) => void; someKeys?: string; someFn?: (...args: any) => any;}; * * // Expect: "setName | someFn" * type Keys = FunctionKeys<MixedProps>; */ export type FunctionKeys<T extends object> = { [K in keyof T]-?: NonUndefined<T[K]> extends Function ? K : never; }[keyof T]; /** * NonFunctionKeys * @desc Get union type of keys that are non-functions in object type `T` * @example * type MixedProps = {name: string; setName: (name: string) => void; someKeys?: string; someFn?: (...args: any) => any;}; * * // Expect: "name | someKey" * type Keys = NonFunctionKeys<MixedProps>; */ export type NonFunctionKeys<T extends object> = { [K in keyof T]-?: NonUndefined<T[K]> extends Function ? never : K; }[keyof T]; /** * MutableKeys * @desc Get union type of keys that are mutable in object type `T` * Credit: Matt McCutchen * IfEquals it will check if x equals y * https://stackoverflow.com/questions/52443276/how-to-exclude-getter-only-properties-from-type-in-typescript * @example * type Props = { readonly foo: string; bar: number }; * * // Expect: "bar" * type Keys = MutableKeys<Props>; */ export type MutableKeys<T extends object> = { [P in keyof T]-?: IfEquals<{ [Q in P]: T[P]; }, { -readonly [Q in P]: T[P]; }, P>; }[keyof T]; export type WritableKeys<T extends object> = MutableKeys<T>; /** * SetDifference (same as Exclude) * @desc Set difference of given union types `A` and `B` * @example * // Expect: "1" * SetDifference<'1' | '2' | '3', '2' | '3' | '4'>; * * // Expect: string | number * SetDifference<string | number | (() => void), Function>; */ export type SetDifference<A, B> = A extends B ? never : A; /** * Diff * @desc From `T` remove properties that exist in `U` * @example * type Props = { name: string; age: number; visible: boolean }; * type DefaultProps = { age: number }; * * // Expect: { name: string; visible: boolean; } * type DiffProps = Diff<Props, DefaultProps>; */ export type Diff<T extends object, U extends object> = Pick<T, SetDifference<keyof T, keyof U>>; /** * RequiredKeys * @desc Get union type of keys that are required in object type `T` * @see https://stackoverflow.com/questions/52984808/is-there-a-way-to-get-all-required-properties-of-a-typescript-object * @example * type Props = { req: number; reqUndef: number | undefined; opt?: string; optUndef?: number | undefined; }; * * // Expect: "req" | "reqUndef" * type Keys = RequiredKeys<Props>; */ export type RequiredKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? never : K; }[keyof T]; /** * OptionalKeys * @desc Get union type of keys that are optional in object type `T` * @see https://stackoverflow.com/questions/52984808/is-there-a-way-to-get-all-required-properties-of-a-typescript-object * @example * type Props = { req: number; reqUndef: number | undefined; opt?: string; optUndef?: number | undefined; }; * * // Expect: "opt" | "optUndef" * type Keys = OptionalKeys<Props>; */ export type OptionalKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? K : never; }[keyof T]; /** * SetComplement * @desc Set complement of given union types `A` and (it's subset) `A1` * @example * // Expect: "1" * SetComplement<'1' | '2' | '3', '2' | '3'>; */ export type SetComplement<A, A1 extends A> = SetDifference<A, A1>; /** * Subtract * @desc From `T` remove properties that exist in `T1` (`T1` has a subset of the properties of `T`) * @example * type Props = { name: string; age: number; visible: boolean }; * type DefaultProps = { age: number }; * * // Expect: { name: string; visible: boolean; } * type RestProps = Subtract<Props, DefaultProps>; */ export type Subtract<T extends T1, T1 extends object> = Pick<T, SetComplement<keyof T, keyof T1>>; /** * Intersection * @desc From `T` pick properties that exist in `U` * @example * type Props = { name: string; age: number; visible: boolean }; * type DefaultProps = { age: number }; * * // Expect: { age: number; } * type DuplicateProps = Intersection<Props, DefaultProps>; */ export type Intersection<T extends object, U extends object> = Pick<T, Extract<keyof T, keyof U> & Extract<keyof U, keyof T>>; /** * Overwrite * @desc From `U` overwrite properties to `T` * @example * type Props = { name: string; age: number; visible: boolean }; * type NewProps = { age: string; other: string }; * * // Expect: { name: string; age: string; visible: boolean; } * type ReplacedProps = Overwrite<Props, NewProps>; */ export type Overwrite<T extends object, U extends object, I = Diff<T, U> & Intersection<U, T>> = Pick<I, keyof I>; /** * Assign * @desc From `U` assign properties to `T` (just like object assign) * @example * type Props = { name: string; age: number; visible: boolean }; * type NewProps = { age: string; other: string }; * * // Expect: { name: string; age: number; visible: boolean; other: string; } * type ExtendedProps = Assign<Props, NewProps>; */ export type Assign<T extends object, U extends object, I = Diff<T, U> & Intersection<U, T> & Diff<U, T>> = Pick<I, keyof I>; /** * Exact * @desc Create branded object type for exact type matching */ export type Exact<A extends object> = A & { __brand: keyof A; }; /** * Unionize * @desc Disjoin object to form union of objects, each with single property * @example * type Props = { name: string; age: number; visible: boolean }; * * // Expect: { name: string; } | { age: number; } | { visible: boolean; } * type UnionizedType = Unionize<Props>; */ export type Unionize<T extends object> = { [P in keyof T]: { [Q in P]: T[P]; }; }[keyof T]; /** * PromiseType * @desc Obtain Promise resolve type * @example * // Expect: string; * type Response = PromiseType<Promise<string>>; */ export type PromiseType<T extends Promise<any>> = T extends Promise<infer U> ? U : never; /** * DeepReadonly * @desc Readonly that works for deeply nested structure * @example * // Expect: { * // readonly first: { * // readonly second: { * // readonly name: string; * // }; * // }; * // } * type NestedProps = { * first: { * second: { * name: string; * }; * }; * }; * type ReadonlyNestedProps = DeepReadonly<NestedProps>; */ export type DeepReadonly<T> = T extends ((...args: any[]) => any) | Primitive ? T : T extends _DeepReadonlyArray<infer U> ? _DeepReadonlyArray<U> : T extends _DeepReadonlyObject<infer V> ? _DeepReadonlyObject<V> : T; /** @private */ export interface _DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> { } /** @private */ export type _DeepReadonlyObject<T> = { readonly [P in keyof T]: DeepReadonly<T[P]>; }; /** * DeepRequired * @desc Required that works for deeply nested structure * @example * // Expect: { * // first: { * // second: { * // name: string; * // }; * // }; * // } * type NestedProps = { * first?: { * second?: { * name?: string; * }; * }; * }; * type RequiredNestedProps = DeepRequired<NestedProps>; */ export type DeepRequired<T> = T extends (...args: any[]) => any ? T : T extends any[] ? _DeepRequiredArray<T[number]> : T extends object ? _DeepRequiredObject<T> : T; /** @private */ export interface _DeepRequiredArray<T> extends Array<DeepRequired<NonUndefined<T>>> { } /** @private */ export type _DeepRequiredObject<T> = { [P in keyof T]-?: DeepRequired<NonUndefined<T[P]>>; }; /** * DeepNonNullable * @desc NonNullable that works for deeply nested structure * @example * // Expect: { * // first: { * // second: { * // name: string; * // }; * // }; * // } * type NestedProps = { * first?: null | { * second?: null | { * name?: string | null | * undefined; * }; * }; * }; * type RequiredNestedProps = DeepNonNullable<NestedProps>; */ export type DeepNonNullable<T> = T extends (...args: any[]) => any ? T : T extends any[] ? _DeepNonNullableArray<T[number]> : T extends object ? _DeepNonNullableObject<T> : T; /** @private */ export interface _DeepNonNullableArray<T> extends Array<DeepNonNullable<NonNullable<T>>> { } /** @private */ export type _DeepNonNullableObject<T> = { [P in keyof T]-?: DeepNonNullable<NonNullable<T[P]>>; }; /** * DeepPartial * @desc Partial that works for deeply nested structure * @example * // Expect: { * // first?: { * // second?: { * // name?: string; * // }; * // }; * // } * type NestedProps = { * first: { * second: { * name: string; * }; * }; * }; * type PartialNestedProps = DeepPartial<NestedProps>; */ export type DeepPartial<T> = { [P in keyof T]?: _DeepPartial<T[P]>; }; /** @private */ export type _DeepPartial<T> = T extends Function ? T : T extends Array<infer U> ? _DeepPartialArray<U> : T extends object ? DeepPartial<T> : T | undefined; /** @private */ export interface _DeepPartialArray<T> extends Array<_DeepPartial<T>> { } export type UnknownFunction = (...params: unknown[]) => unknown; export type NonNullableObject<T> = T extends null | undefined ? {} : T; export type NonArrayObject<T> = T extends object ? (T extends any[] ? {} : T) : {};