Skip to content

Instantly share code, notes, and snippets.

@trvswgnr
Created November 27, 2024 00:32
Show Gist options
  • Select an option

  • Save trvswgnr/3882992a785af84d9e8459c7615b8f52 to your computer and use it in GitHub Desktop.

Select an option

Save trvswgnr/3882992a785af84d9e8459c7615b8f52 to your computer and use it in GitHub Desktop.

Revisions

  1. trvswgnr created this gist Nov 27, 2024.
    89 changes: 89 additions & 0 deletions partial-application.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,89 @@
    import type { Expect, Equal, IsAny, IsUnknown } from "type-testing";
    import { Solution } from "./solution";

    // biome-ignore lint/suspicious/noExplicitAny: <explanation>
    type AnyArray = any[];

    type PartialApplicationHelper<T extends AnyArray, R> = <A extends Partial<T>>(
    ...args: A
    ) => A extends AnyArray
    ? A["length"] extends T["length"]
    ? R
    : PartialApplicationHelper<ShiftN<T, A["length"]>, R>
    : never;

    type PartialApplication<T extends (...args: AnyArray) => unknown> =
    PartialApplicationHelper<Parameters<T>, ReturnType<T>>;

    type ToArray<T extends number, R extends AnyArray = []> = R["length"] extends T
    ? R
    : ToArray<T, [...R, unknown]>;
    type Subtract<M extends number, S extends number> = ToArray<M> extends [
    ...ToArray<S>,
    ...infer Rest,
    ]
    ? Rest["length"]
    : never;

    type ShiftN<T extends AnyArray, N extends number> = T extends [
    infer _First,
    ...infer Rest extends AnyArray,
    ]
    ? N extends 0
    ? T
    : ShiftN<Rest, N extends 0 ? 0 : Subtract<N, 1>>
    : never;

    // Sample function with multiple arguments
    const f = (arg1: number, arg2: string, arg3: boolean, arg4: Date) => {
    return `${arg1}, ${arg2}, ${arg3}, ${arg4.toDateString()}`;
    };

    const partial = <T extends (...args: Parameters<T>) => ReturnType<T>>(
    fn: T,
    ) => {
    // TODO: a real implementation here
    return fn as unknown as PartialApplication<T>;
    };

    const z: PartialApplication<typeof f> = partial(f);

    // Example calls: all should not be errors
    const result1 = z(0, "a", true, new Date()); // Direct call
    const result2 = z(0)("a", true, new Date()); // Partially applied
    const result3 = z(0, "a", true)(new Date()); // Another partial
    const result4 = z()(0, "a", true)(new Date()); // Another partial
    const result5 = z(0, "a", true); // fn missing last arg

    type IsUnknownOrAny<T> = IsUnknown<T> extends true
    ? true
    : IsAny<T> extends true
    ? true
    : false;

    type HasUnknownOrAny<T extends readonly unknown[]> = T extends [
    infer First,
    ...infer Rest,
    ]
    ? IsUnknownOrAny<First> extends true
    ? true
    : HasUnknownOrAny<Rest>
    : false;

    type t0 = Expect<Equal<HasUnknownOrAny<Parameters<typeof f>>, false>>;

    type t1_actual = [
    typeof result1,
    typeof result2,
    typeof result3,
    typeof result4,
    typeof result5,
    ];
    type t1_expected = [
    string,
    string,
    string,
    string,
    PartialApplicationHelper<[x: Date], string>,
    ];
    type t1 = Expect<Equal<t1_actual, t1_expected>>;