typedash
Version:
modern, type-safe collection of utility functions
67 lines (63 loc) • 2.83 kB
TypeScript
import { Get } from 'type-fest';
import { O as ObjectPath } from '../ObjectPath-DtBUgaHp.js';
import '../AnyFunction-DuIh5Fcc.js';
/**
* Gets all elements except for head in an array
* @example
* Tail<[1, 2, 3]>; // [2, 3]
*/
type Tail<T extends readonly unknown[]> = T extends [unknown, ...infer Rest] ? Rest : never;
/**
* Returns Tail<T> if first element of T matches P. Somehow resolves issues with
* union types, but unsure how; @see {@link https://stackoverflow.com/a/57837897/1105281}
*/
type TailUnion<P, T extends readonly unknown[]> = T extends readonly unknown[] ? T[0] extends P ? Tail<T> : never : never;
/**
* Converts a string like "a.b.c" to an array ["a", "b", "c"]
*/
type PathToStringArray<T extends string> = T extends `${infer Head}.${infer Tail}` ? [...PathToStringArray<Head>, ...PathToStringArray<Tail>] : [T];
/**
* For any object, enforces that the keys provided are all present; paths are
* expressed as lists of keys in the order you would access them on an object;
* for instance, for an object called obj, obj.a.b.c would be represented as
* ["a", "b", "c"]
* @see RequireKeysDeep for usage
*/
type RequireKeysDeepArray<TObject, PathsToRequire extends readonly string[]> = TObject extends object ? Omit<TObject, Extract<keyof TObject, PathsToRequire[0]>> & Required<{
[K in Extract<keyof TObject, PathsToRequire[0]>]: NonNullable<RequireKeysDeepArray<TObject[K], TailUnion<K, PathsToRequire>>>;
}> : TObject;
/**
* For any object, enforces that the keys provided are all present; works with
* deeply nested syntax, by using period (`.`) as a delimiter.
* @example
* ```ts
* type Foo = { a?: 2, b?: { c?: 3, d: 4 } }
* type A = RequireKeysDeep<Foo, "a">; // {a: 2, b?: { c?: 3, d: 4 } }
* type B = RequireKeysDeep<Foo, "b">; // {a?: 2, b: { c?: 3, d: 4 } }
* type BC = RequireKeysDeep<Foo, "b.c">; // {a?: 2, b: { c: 3, d: 4 } }
* type ABC = RequireKeysDeep<Foo, "a" | "b.c">; // {a: 2, b: { c: 3, d: 4 } }
* ```
*/
type RequireKeysDeep<TObject extends object, TPath extends string> = RequireKeysDeepArray<TObject, PathToStringArray<TPath>>;
/**
* Sets a value at the specified (possibly nested) path in an object.
* @template TObject The type of the object.
* @template Path The type of the path.
* @param object The object to set the value in.
* @param path The path to set the value at.
* @param value The value to set.
* @example
* ```ts
* const object: {
* foo?: {
* bar?: {
* anArray: string[];
* };
* }
* };
*
* set(object, 'foo.bar.anArray[0]', 'value');
* object.foo.bar.anArray[0]; // 'value'
*/
declare function set<TObject extends Record<string, unknown>, Path extends ObjectPath<TObject>>(object: TObject, path: Path, value: Get<TObject, Path>): asserts object is TObject & RequireKeysDeep<TObject, Path>;
export { set };