Skip to content

Instantly share code, notes, and snippets.

@DuCanhGH
Last active October 3, 2023 04:33
Show Gist options
  • Save DuCanhGH/c7788ef93bb07070d62ffb928693741f to your computer and use it in GitHub Desktop.
Save DuCanhGH/c7788ef93bb07070d62ffb928693741f to your computer and use it in GitHub Desktop.
utils_types.ts
/**
* Make certain fields in a object type required
*
* @example
* interface A {
* a?: string;
* b?: string;
* c?: string;
* }
* type B = RequireFields<A, "b" | "c">;
* const b: B = {
* b: "hehe",
* c: "hehe",
* }; //valid
* const b: B = { a: "hehe" }; //invalid
* const c: B = { a: "hehe", b: "hehe" }; //invalid
*/
type RequireFields<T, U extends keyof T> = T & Required<Pick<T, U>>;
/**
* Make certain fields in a object type optional
*
* @example
* interface A {
* a: string;
* b: string;
* c: string;
* }
* type B = Optional<A, "b" | "c">;
* const b: B = { a: "hehe" }; //valid
* const b: B = {}; //invalid
*/
type Optional<T, U extends keyof T> = Omit<T, U> & Partial<Pick<T, U>>;
/**
* Add prefix to a string
*
* @example
* type A = WithPrefix<"hehe">;
* const a: A = "heheboy";
* const b: A = "boy"; // invalid
*/
type WithPrefix<T extends string> = `${T}${string}`;
/**
* Get all possible paths to children in an object
*
* @example
* const a = {
* a: {
* b: {
* c: "hehe",
* },
* },
* };
* type A = NestedKeyOf<typeof a>;
* // type A is "a" | "a.b" | "a.b.c"
*/
type NestedKeyOf<T> = T extends Record<string, unknown>
? {
[Key in Extract<keyof T, string>]: T[Key] extends Array<any>
? `${Key}`
: T[Key] extends Record<string, unknown>
? `${Key}` | `${Key}.${NestedKeyOf<T[Key]>}`
: `${Key}`;
}[Extract<keyof T, string>]
: undefined;
/**
* Get all types of children in an object
*
* @example
* const a = {
* a: {
* b: {
* c: "hehe",
* },
* },
* };
* type A = NestedKeyTypes<typeof a, keyof typeof a>;
* // type A is string | { c: string; } | { b: { c: string; }; }
*/
type NestedKeyTypes<T, K extends keyof T> = T extends Record<string, unknown>
? T[K] extends Array<T[K][keyof T[K]]>
? T[K]
: T[K] extends object
? NestedKeyTypes<T[K], keyof T[K]> | T[K]
: T[K]
: undefined;
/**
* Split a string type into an array of strings
*
* @example
* type A = Split<"1.2.3.4", ".">;
* // type A is ["1", "2", "3", "4"]
* const a: A = ["1", "2", "3", "4"]; //this array only allows string "1", "2", "3", "4", nothing else
* type B = Partial<A>;
* // type B is [("1" | undefined)?, ("2" | undefined)?, ("3" | undefined)?, ("4" | undefined)?]
* const b: B = ["1", "2"]; //this array only allows string "1", "2", "3", "4", nothing else, but you can leave some out
*/
type Split<S extends string, D extends string> = string extends S
? string[]
: S extends ""
? []
: S extends `${infer T}${D}${infer U}`
? [T, ...Split<U, D>]
: [S];
/**
* Get type of child at path S in an object
*
* @example
* const a = {
* a: {
* b: {
* c: "hehe",
* },
* },
* };
* type A = GetValueAtPath<typeof a, "a.b.c">;
* // type A is string
* type B = GetValueAtPath<typeof a, "a.b">;
* // type B is { c: string; }
*/
type GetValueAtPath<T, S extends NestedKeyOf<T> | undefined> = T extends Record<string, any>
? S extends `${infer V}.${infer U}`
? T[V] extends Record<string, any>
? U extends NestedKeyOf<T[V]>
? GetValueAtPath<T[V], U>
: undefined
: T[V]
: S extends string
? T[S]
: undefined
: undefined;
export type {
GetValueAtPath,
NestedKeyOf,
NestedKeyTypes,
Optional,
RequireFields,
Split,
WithPrefix,
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment