Skip to content

Instantly share code, notes, and snippets.

@gtkatakura-bysoft
Created January 3, 2019 11:16
Show Gist options
  • Save gtkatakura-bysoft/d45e7407136a25da63599f3436ffc7af to your computer and use it in GitHub Desktop.
Save gtkatakura-bysoft/d45e7407136a25da63599f3436ffc7af to your computer and use it in GitHub Desktop.
```ts
type Tail<F extends Function, S extends Number> =
S extends 0 ? (F extends (...args: infer TArgs) => any ? TArgs : never) :
S extends 1 ? (F extends (a: any, ...args: infer TArgs) => any ? TArgs : never) :
S extends 2 ? (F extends (a: any, b: any, ...args: infer TArgs) => any ? TArgs : never) :
S extends 3 ? (F extends (a: any, b: any, c: any, ...args: infer TArgs) => any ? TArgs : never) :
S extends 4 ? (F extends (a: any, b: any, c: any, d: any, ...args: infer TArgs) => any ? TArgs : never) :
S extends 5 ? (F extends (a: any, b: any, c: any, d: any, e: any, ...args: infer TArgs) => any ? TArgs : never) :
S extends 6 ? (F extends (a: any, b: any, c: any, d: any, e: any, f: any, ...args: infer TArgs) => any ? TArgs : never) :
never
type TailArray<A extends any[], S extends Number> =
Tail<(...args: A) => any, S>
type Args<T extends Function> =
T extends (...args: infer TArgs) => any ? TArgs
: never
type PartialArgs<T extends Function> =
T extends (...args: infer TArgs) => any ? Partial<TArgs>
: never
type Curried<T extends (...args: any) => any, TReturn = ReturnType<T>> =
<
TArgs extends PartialArgs<T>,
TRest extends TailArray<Args<T>, TArgs['length']>
>(...args: TArgs) =>
TRest extends []
? TReturn
: Curried<(...args: TRest) => TReturn>
type Curry = <TFunc extends (...args: any) => any>(func: TFunc) => Curried<TFunc>
declare const curry: Curry
const curried = curry((a: 1 | undefined, b: number | number[], c: string) => 1)
// works :D
const a = curried(1)([2])('x')
const b = curried(1)(2, 'x')
const c = curried(1, 2)('x')
const d = curried(1, 2, 'x')
// the only problem is that `undefined` is accepted
// Partial<[1, 2]> => [1 | undefined, 2 | undefined]
curried(undefined)(2)(undefined)
```
```ts
type Init<T extends any[], TTail extends any[] = TailArray<T>> = CastArray<{
[K in keyof TTail]: T[keyof T & K];
}>
type PotentialArgs<T extends any[], TResult extends any[] = T> = {
"continue": PotentialArgs<Init<T>, TResult | Init<T>>;
"end": TResult;
}[T extends [] ? "end" : "continue"]
type Args<T extends Function> =
T extends (...args: infer TArgs) => any ? TArgs
: never
type TailArgs<F extends Function> =
F extends (head: any, ...tail: infer TTail) => any ? TTail :
never
type TailArray<A extends any[]> = TailArgs<(...args: A) => any>
type CastArray<T> = T extends any[] ? T : []
type DropFromArraySize<TSource extends any[], TSize extends any[]> = CastArray<{
"continue": DropFromArraySize<TailArray<TSource>, TailArray<TSize>>,
"end": TSource,
}[TSize extends [] ? "end" : "continue"]>
type PartialArgs<T extends Function> =
T extends (...args: infer TArgs) => any ? Partial<TArgs>
: never
type Curried<T extends (...args: any) => any> =
<
TInitArgs extends PotentialArgs<Args<T>>,
TTailArgs extends DropFromArraySize<Args<T>, TInitArgs>
>(...args: TInitArgs) =>
TTailArgs extends []
? ReturnType<T>
: Curried<(...args: TTailArgs) => ReturnType<T>>
type Curry = <TFunc extends (...args: any) => any>(func: TFunc) => Curried<TFunc>
declare const curry: Curry
const curried = curry((a: 1 | undefined, b: number | number[], c: string) => 1)
// works :D
const a = curried(1)([2])('x')
const b = curried(1)(2, 'x')
const c = curried(1, 2)('x')
const d = curried(1, 2, 'x')
const e = curried(undefined)(2)('2')
curried(undefined)(2)(undefined) // notify error
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment