hd-utils
Version:
A handy utils for modern JS developers
358 lines (357 loc) • 12.1 kB
TypeScript
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) : {};