Merge pull request #72 from Beraliv/feat/partial-deep
refact: PartialDeep alignment for different structures
This commit is contained in:
@@ -339,7 +339,7 @@ export namespace Objects {
|
||||
* @returns The object with its properties made readonly
|
||||
* @example
|
||||
* ```ts
|
||||
* type T0 = Call<Objects.Readonly, {a: 1; b: true }>; // { readonly a:1; readonly b: true}
|
||||
* type T0 = Call<Objects.Readonly, { a: 1; b: true }>; // { readonly a:1; readonly b: true}
|
||||
* type T1 = Eval<Objects.Readonly<{ a: 1; b: true }>>; // { readonly a:1; readonly b: true}
|
||||
* ```
|
||||
*/
|
||||
@@ -356,7 +356,7 @@ export namespace Objects {
|
||||
* @returns The object with its properties made required
|
||||
* @example
|
||||
* ```ts
|
||||
* type T0 = Call<Objects.Required, {a?: 1; b?: true }>; // { a:1; b: true}
|
||||
* type T0 = Call<Objects.Required, { a?: 1; b?: true }>; // { a:1; b: true}
|
||||
* type T1 = Eval<Objects.Required<{ a?: 1; b?: true }>>; // { a:1; b: true}
|
||||
* ```
|
||||
*/
|
||||
@@ -373,7 +373,7 @@ export namespace Objects {
|
||||
* @returns The object with its properties made optional
|
||||
* @example
|
||||
* ```ts
|
||||
* type T0 = Call<Objects.Partial, {a: 1; b: true }>; // { a?:1; b?: true}
|
||||
* type T0 = Call<Objects.Partial, { a: 1; b: true }>; // { a?:1; b?: true}
|
||||
* type T1 = Eval<Objects.Partial<{ a: 1; b: true }>>; // { a?:1; b?: true}
|
||||
* ```
|
||||
*/
|
||||
@@ -383,6 +383,23 @@ export namespace Objects {
|
||||
return: this["args"] extends [infer value] ? Std._Partial<value> : never;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make all properties of an object mutable
|
||||
* @param value - The object to make properties mutable
|
||||
* @returns The object with its properties made mutable
|
||||
* @example
|
||||
* ```ts
|
||||
* type T0 = Call<Objects.Mutable, { readonly a: 1; readonly b: true }>; // { a:1; b: true }
|
||||
* ```
|
||||
*/
|
||||
export type Mutable<obj = unset> = PartialApply<MutableFn, [obj]>;
|
||||
|
||||
interface MutableFn extends Fn {
|
||||
return: this["args"] extends [infer obj, ...any]
|
||||
? { -readonly [key in keyof obj]: obj[key] }
|
||||
: never;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes all levels of an object optional
|
||||
* @description This function is used to make all levels of an object optional
|
||||
@@ -391,15 +408,88 @@ export namespace Objects {
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* type T0 = Call<Objects.PartialDeep, {a: 1; b: true }>; // { a?:1; b?: true}
|
||||
* type T1 = Call<Objects.PartialDeep, {a: 1; b: { c: true } }>; // { a?:1; b?: { c?: true } }
|
||||
* type T2 = Call<Objects.PartialDeep, {a: 1; b: { c: true, d: { e: false } } }>; // { a?:1; b?: { c?: true, d?: { e?: false } } }
|
||||
* type T0 = Call<Objects.PartialDeep, { a: 1; b: true }>;
|
||||
* // ^? { a?:1; b?: true}
|
||||
* type T1 = Call<Objects.PartialDeep, { a: 1; b: { c: true } }>;
|
||||
* // ^? { a?:1; b?: { c?: true } }
|
||||
* type T2 = Call<Objects.PartialDeep, { a: 1; b: { c: true, d: { e: false } } }>;
|
||||
* // ^? { a?:1; b?: { c?: true, d?: { e?: false } } }
|
||||
*/
|
||||
|
||||
export type PartialDeep<obj = unset> = PartialApply<PartialDeepFn, [obj]>;
|
||||
|
||||
interface PartialDeepFn extends Fn {
|
||||
return: this["args"] extends [infer obj] ? Impl.PartialDeep<obj> : never;
|
||||
return: this["args"] extends [infer obj]
|
||||
? Impl.TransformObjectDeep<PartialFn, obj>
|
||||
: never;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes all levels of an object required
|
||||
* @description This function is used to make all levels of an object required
|
||||
* @param obj - The object to make levels required
|
||||
* @returns The object with its levels made required
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* type T0 = Call<Objects.RequiredDeep, { a?:1; b?: true }>;
|
||||
* // ^? { a: 1; b: true }
|
||||
* type T1 = Call<Objects.RequiredDeep, { a?:1; b?: { c?: true } }>;
|
||||
* // ^? { a: 1; b: { c: true } }
|
||||
* type T2 = Call<Objects.RequiredDeep, { a?:1; b?: { c?: true, d?: { e?: false } } }>;
|
||||
* // ^? { a: 1; b: { c: true, d: { e: false } } }
|
||||
*/
|
||||
export type RequiredDeep<obj = unset> = PartialApply<RequiredDeepFn, [obj]>;
|
||||
|
||||
interface RequiredDeepFn extends Fn {
|
||||
return: this["args"] extends [infer obj]
|
||||
? Impl.TransformObjectDeep<RequiredFn, obj>
|
||||
: never;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes all levels of an object readonly
|
||||
* @description This function is used to make all levels of an object readonly
|
||||
* @param obj - The object to make levels readonly
|
||||
* @returns The object with its levels made readonly
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* type T0 = Call<Objects.ReadonlyDeep, { a:1; b: true }>;
|
||||
* // ^? { readonly a: 1; readonly b: true }
|
||||
* type T1 = Call<Objects.ReadonlyDeep, { a:1; b: { c: true } }>;
|
||||
* // ^? { readonly a: 1; readonly b: { readonly c: true } }
|
||||
* type T2 = Call<Objects.ReadonlyDeep, { a:1; b: { c: true, d: { e: false } } }>;
|
||||
* // ^? { readonly a: 1; readonly b: { readonly c: true, d: { readonly e: false } } }
|
||||
*/
|
||||
export type ReadonlyDeep<obj = unset> = PartialApply<ReadonlyDeepFn, [obj]>;
|
||||
|
||||
interface ReadonlyDeepFn extends Fn {
|
||||
return: this["args"] extends [infer obj]
|
||||
? Impl.TransformObjectDeep<ReadonlyFn, obj>
|
||||
: never;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes all levels of an object mutable
|
||||
* @description This function is used to make all levels of an object mutable
|
||||
* @param obj - The object to make levels mutable
|
||||
* @returns The object with its levels made mutable
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* type T0 = Call<Objects.MutableDeep, { readonly a: 1; readonly b: true }>;
|
||||
* // ^? { a:1; b: true }
|
||||
* type T1 = Call<Objects.MutableDeep, { readonly a: 1; readonly b: { readonly c: true } }>;
|
||||
* // ^? { a:1; b: { c: true } }
|
||||
* type T2 = Call<Objects.MutableDeep, { readonly a: 1; readonly b: { readonly c: true, d: { readonly e: false } } }>;
|
||||
* // ^? { a:1; b: { c: true, d: { e: false } } }
|
||||
*/
|
||||
export type MutableDeep<obj = unset> = PartialApply<MutableDeepFn, [obj]>;
|
||||
|
||||
interface MutableDeepFn extends Fn {
|
||||
return: this["args"] extends [infer obj]
|
||||
? Impl.TransformObjectDeep<MutableFn, obj>
|
||||
: never;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { Apply, Call, Fn } from "../../core/Core";
|
||||
import { Strings } from "../../strings/Strings";
|
||||
import { Equal, Prettify, Primitive, UnionToIntersection } from "../../helpers";
|
||||
import {
|
||||
Equal,
|
||||
IsTuple,
|
||||
Prettify,
|
||||
Primitive,
|
||||
UnionToIntersection,
|
||||
} from "../../helpers";
|
||||
|
||||
export type Keys<src> = src extends readonly unknown[]
|
||||
? {
|
||||
@@ -58,9 +64,36 @@ type RecursiveGet<Obj, pathList> = Obj extends any
|
||||
: Obj
|
||||
: never;
|
||||
|
||||
export type PartialDeep<T> = T extends object
|
||||
? { [P in keyof T]?: PartialDeep<T[P]> }
|
||||
: T;
|
||||
export type TransformObjectDeep<fn extends Fn, type> = type extends
|
||||
| Function
|
||||
| Date
|
||||
? type
|
||||
: type extends Map<infer keys, infer values>
|
||||
? Map<TransformObjectDeep<fn, keys>, TransformObjectDeep<fn, values>>
|
||||
: type extends ReadonlyMap<infer keys, infer values>
|
||||
? ReadonlyMap<TransformObjectDeep<fn, keys>, TransformObjectDeep<fn, values>>
|
||||
: type extends WeakMap<infer keys, infer values>
|
||||
? WeakMap<
|
||||
Extract<TransformObjectDeep<fn, keys>, object>,
|
||||
TransformObjectDeep<fn, values>
|
||||
>
|
||||
: type extends Set<infer values>
|
||||
? Set<TransformObjectDeep<fn, values>>
|
||||
: type extends ReadonlySet<infer values>
|
||||
? ReadonlySet<TransformObjectDeep<fn, values>>
|
||||
: type extends WeakSet<infer values>
|
||||
? WeakSet<Extract<TransformObjectDeep<fn, values>, object>>
|
||||
: type extends Array<infer values>
|
||||
? IsTuple<type> extends true
|
||||
? Call<fn, { [Key in keyof type]: TransformObjectDeep<fn, type[Key]> }>
|
||||
: Array<TransformObjectDeep<fn, values> | undefined>
|
||||
: type extends Promise<infer value>
|
||||
? Promise<TransformObjectDeep<fn, value>>
|
||||
: type extends object
|
||||
? Call<fn, { [Key in keyof type]: TransformObjectDeep<fn, type[Key]> }>
|
||||
: Equal<type, unknown> extends true
|
||||
? unknown
|
||||
: Partial<type>;
|
||||
|
||||
export type Update<obj, path, fnOrValue> = RecursiveUpdate<
|
||||
obj,
|
||||
|
||||
@@ -95,47 +95,6 @@ describe("Objects", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("PartialDeep", () => {
|
||||
type res0 = Call<Objects.PartialDeep, { a: 1; b: 2 }>;
|
||||
// ^?
|
||||
type test0 = Expect<Equal<res0, { a?: 1; b?: 2 }>>;
|
||||
|
||||
type res1 = Call<Objects.PartialDeep, { a: 1; b: { c: 2 } }>;
|
||||
// ^?
|
||||
type test1 = Expect<Equal<res1, { a?: 1; b?: { c?: 2 } }>>;
|
||||
|
||||
type res2 = Call<Objects.PartialDeep, { a: 1; b: { c: 2; d: { e: 3 } } }>;
|
||||
// ^?
|
||||
type test2 = Expect<Equal<res2, { a?: 1; b?: { c?: 2; d?: { e?: 3 } } }>>;
|
||||
|
||||
type tuple = [string, number];
|
||||
type res3 = Call<Objects.PartialDeep, tuple>;
|
||||
// ^?
|
||||
type test3 = Expect<Equal<res3, [string?, number?]>>;
|
||||
|
||||
type res4 = Call<Objects.PartialDeep, [string, tuple]>;
|
||||
// ^?
|
||||
type test4 = Expect<Equal<res4, [string?, [string?, number?]?]>>;
|
||||
|
||||
type res5 = Call<
|
||||
Objects.PartialDeep,
|
||||
{ tuple: tuple; tuple2: { tuple3: tuple } }
|
||||
>;
|
||||
// ^?
|
||||
type test5 = Expect<
|
||||
Equal<
|
||||
res5,
|
||||
{ tuple?: [string?, number?]; tuple2?: { tuple3?: [string?, number?] } }
|
||||
>
|
||||
>;
|
||||
|
||||
type res6 = Call<Objects.PartialDeep, { tuple: [string, tuple] }>;
|
||||
// ^?
|
||||
type test6 = Expect<
|
||||
Equal<res6, { tuple?: [string?, [string?, number?]?] }>
|
||||
>;
|
||||
});
|
||||
|
||||
describe("Update", () => {
|
||||
it("basic", () => {
|
||||
type res0 = Call<Objects.Update<"a", Numbers.Add<1>>, { a: 1; b: 1 }>;
|
||||
@@ -301,6 +260,172 @@ describe("Objects", () => {
|
||||
type test1 = Expect<Equal<res1, { b: true }>>;
|
||||
});
|
||||
|
||||
describe("PartialDeep", () => {
|
||||
it("primitives", () => {
|
||||
type res0 = Call<Objects.PartialDeep, number>;
|
||||
type test0 = Expect<Equal<res0, number>>;
|
||||
|
||||
type res1 = Call<Objects.PartialDeep, string>;
|
||||
type test1 = Expect<Equal<res1, string>>;
|
||||
|
||||
type res2 = Call<Objects.PartialDeep, boolean>;
|
||||
type test2 = Expect<Equal<res2, boolean>>;
|
||||
|
||||
type res3 = Call<Objects.PartialDeep, bigint>;
|
||||
type test3 = Expect<Equal<res3, bigint>>;
|
||||
|
||||
type res4 = Call<Objects.PartialDeep, symbol>;
|
||||
type test4 = Expect<Equal<res4, symbol>>;
|
||||
|
||||
type res5 = Call<Objects.PartialDeep, undefined>;
|
||||
type test5 = Expect<Equal<res5, undefined>>;
|
||||
|
||||
type res6 = Call<Objects.PartialDeep, null>;
|
||||
type test6 = Expect<Equal<res6, null>>;
|
||||
|
||||
type res7 = Call<Objects.PartialDeep, Function>;
|
||||
type test7 = Expect<Equal<res7, Function>>;
|
||||
});
|
||||
|
||||
it("Map & Set", () => {
|
||||
type res0 = Call<Objects.PartialDeep, Map<string, boolean>>;
|
||||
type test0 = Expect<Equal<res0, Map<string, boolean>>>;
|
||||
|
||||
type res1 = Call<Objects.PartialDeep, Map<string, { a: number }>>;
|
||||
type test1 = Expect<Equal<res1, Map<string, { a?: number }>>>;
|
||||
|
||||
type res2 = Call<Objects.PartialDeep, ReadonlyMap<string, boolean>>;
|
||||
type test2 = Expect<Equal<res2, ReadonlyMap<string, boolean>>>;
|
||||
|
||||
type res3 = Call<
|
||||
Objects.PartialDeep,
|
||||
ReadonlyMap<string, { checked: boolean }>
|
||||
>;
|
||||
type test3 = Expect<
|
||||
Equal<res3, ReadonlyMap<string, { checked?: boolean }>>
|
||||
>;
|
||||
|
||||
type res4 = Call<Objects.PartialDeep, WeakMap<{ key: string }, boolean>>;
|
||||
type test4 = Expect<Equal<res4, WeakMap<{ key?: string }, boolean>>>;
|
||||
|
||||
type res5 = Call<
|
||||
Objects.PartialDeep,
|
||||
WeakMap<{ key: string }, { value: boolean }>
|
||||
>;
|
||||
type test5 = Expect<
|
||||
Equal<res5, WeakMap<{ key?: string }, { value?: boolean }>>
|
||||
>;
|
||||
|
||||
type res6 = Call<Objects.PartialDeep, Set<string>>;
|
||||
type test6 = Expect<Equal<res6, Set<string>>>;
|
||||
|
||||
type res7 = Call<Objects.PartialDeep, Set<number[]>>;
|
||||
type test7 = Expect<Equal<res7, Set<(number | undefined)[]>>>;
|
||||
|
||||
type res8 = Call<Objects.PartialDeep, ReadonlySet<string>>;
|
||||
type test8 = Expect<Equal<res8, ReadonlySet<string>>>;
|
||||
});
|
||||
|
||||
it("Objects and Arrays", () => {
|
||||
type res1 = Call<Objects.PartialDeep, []>;
|
||||
type test1 = Expect<Equal<res1, []>>;
|
||||
|
||||
type res2 = Call<Objects.PartialDeep, never[]>;
|
||||
type test2 = Expect<Equal<res2, undefined[]>>;
|
||||
|
||||
type res3 = Call<Objects.PartialDeep, [1, 2, 3]>;
|
||||
type test3 = Expect<
|
||||
Equal<res3, [(1 | undefined)?, (2 | undefined)?, (3 | undefined)?]>
|
||||
>;
|
||||
|
||||
type res4 = Call<Objects.PartialDeep, readonly number[]>;
|
||||
type test4 = Expect<Equal<res4, readonly (number | undefined)[]>>;
|
||||
|
||||
type res5 = Call<Objects.PartialDeep, number[]>;
|
||||
type test5 = Expect<Equal<res5, (number | undefined)[]>>;
|
||||
|
||||
type res6 = Call<Objects.PartialDeep, Array<number>>;
|
||||
type test6 = Expect<Equal<res6, Array<number | undefined>>>;
|
||||
|
||||
type res7 = Call<
|
||||
Objects.PartialDeep,
|
||||
{ readonly obj: unknown; readonly arr: readonly unknown[] }
|
||||
>;
|
||||
type test7 = Expect<
|
||||
Equal<
|
||||
res7,
|
||||
{
|
||||
readonly obj?: unknown | undefined;
|
||||
readonly arr?: readonly unknown[] | undefined;
|
||||
}
|
||||
>
|
||||
>;
|
||||
|
||||
type res8 = Call<Objects.PartialDeep, { a: 1; b: 2; c: 3 }>;
|
||||
type test8 = Expect<Equal<res8, { a?: 1; b?: 2; c?: 3 }>>;
|
||||
|
||||
type res9 = Call<Objects.PartialDeep, { foo: () => void }>;
|
||||
type test9 = Expect<Equal<res9, { foo?: () => void }>>;
|
||||
});
|
||||
|
||||
it("Promises", () => {
|
||||
type res0 = Call<Objects.PartialDeep, Promise<number>>;
|
||||
type test0 = Expect<Equal<res0, Promise<number>>>;
|
||||
|
||||
type res1 = Call<
|
||||
Objects.PartialDeep,
|
||||
Promise<{ api: () => { play: () => void; pause: () => void } }>
|
||||
>;
|
||||
type test1 = Expect<
|
||||
Equal<
|
||||
res1,
|
||||
Promise<{ api?: () => { play: () => void; pause: () => void } }>
|
||||
>
|
||||
>;
|
||||
});
|
||||
|
||||
it("Complex structures", () => {
|
||||
type ComplexNestedRequired = {
|
||||
simple: number;
|
||||
nested: {
|
||||
date: Date;
|
||||
func: () => string;
|
||||
array: { bar: number }[];
|
||||
tuple: [string, number, { good: boolean }];
|
||||
set: Set<{ name: string }>;
|
||||
map: Map<
|
||||
string,
|
||||
{
|
||||
name: string;
|
||||
}
|
||||
>;
|
||||
promise: Promise<{ foo: string; bar: number }>;
|
||||
};
|
||||
};
|
||||
|
||||
type ComplexNestedPartial = {
|
||||
simple?: number;
|
||||
nested?: {
|
||||
date?: Date;
|
||||
func?: () => string;
|
||||
array?: ({ bar?: number } | undefined)[];
|
||||
set?: Set<{ name?: string }>;
|
||||
tuple?: [string?, number?, { good?: boolean }?];
|
||||
map?: Map<
|
||||
string,
|
||||
{
|
||||
name?: string;
|
||||
}
|
||||
>;
|
||||
promise?: Promise<{ foo?: string; bar?: number }>;
|
||||
};
|
||||
};
|
||||
|
||||
type res1 = Call<Objects.PartialDeep, ComplexNestedRequired>;
|
||||
type test1 = Expect<Equal<res1, ComplexNestedPartial>>;
|
||||
});
|
||||
});
|
||||
|
||||
describe("Assign", () => {
|
||||
it("can be called without any pre-filled arguments", () => {
|
||||
type res1 = Call<
|
||||
|
||||
Reference in New Issue
Block a user