Last active
January 11, 2019 00:38
-
-
Save gtkatakura/4f5ec735a4747fb2b9d1f09cf3e5598e to your computer and use it in GitHub Desktop.
gtkatakura-bysoft
commented
Aug 21, 2018
•
- https://gist.github.com/MarcelCutts/f90d14cfc11dcfae567375aac960617a
- The Little Typer
Solution for one thread twitter.
- Solution 1:
type Func<TArgs extends any[] = any[], TResult = any> = (...args: TArgs) => TResult
type ArgumentsFrom<T> = T extends Func<infer TArgs> ? TArgs : never;
type MethodNamesFrom<T> = {
[TKey in keyof T]: T[TKey] extends Func ? TKey : never
}[keyof T]
function execute<
TObject extends object,
TKey extends string & MethodNamesFrom<TObject>,
TArgs extends ArgumentsFrom<TObject[TKey]>,
TMethod extends Func & TObject[TKey],
>(
object: TObject,
methodName: TKey,
args: TArgs
): ReturnType<TMethod> {
const method = object[methodName];
if (typeof method !== 'function') {
throw new Error('not a function')
}
return method(...args)
}
- Solution 2:
type Func<TArgs extends any[] = any[], TResult = any> = (...args: TArgs) => TResult
type ArgumentsFrom<T> = T extends Func<infer TArgs> ? TArgs : never;
type MethodNamesFrom<T> = {
[TKey in keyof T]: T[TKey] extends Func ? TKey : never
}[keyof T]
function execute<
TObject extends object,
TKey extends string & MethodNamesFrom<TObject>,
TMethod extends Func & TObject[TKey],
>(
object: TObject,
methodName: TKey,
args: Parameters<TMethod>
): ReturnType<TMethod> {
const method = object[methodName];
if (typeof method !== 'function') {
throw new Error('not a function')
}
return method(...args)
}
- Tests:
interface Test {
foo: (a: number, b: number) => number;
fee: (a: number, b: string) => number;
bar: string;
}
const test: Test = {
foo: (a, b) => a + b,
fee: (a, b) => a,
bar: 'not a function'
}
execute(test, 'foo', [1, 2])
execute(test, 'fee', [1, '2'])
execute(test, 'bar', [1, '2']) // <= error in 'methodName'
execute(test, 'foo', [1]) // <= error in 'args'
execute(test, 'foo', [1, 2, 3]) // <= error in 'args'
execute(test, 'foo', [1, '3']) // <= error in 'args'
execute(test, 'fee', [1]) // <= error in 'args'
execute(test, 'fee', [1, '2', '3']) // <= error in 'args'
execute(test, 'fee', [1, 2]) // <= error in 'args'
// Tuple operations Cons, Head, Tail
type Head<T> = T extends [infer U, ...unknown[]] ? U : never
type Tail<T> = T extends Array<any>
? ((...args: T) => never) extends ((a: any, ...args: infer R) => never)
? R
: never
: never
type Cons<T extends any[], H> = ((h: H, ...t: T) => any) extends ((...x: infer X) => any) ? X : never
// Generic lazy tuple reduction
interface Reduction<Base, In> {
0: Base
1: In
}
type Reduce<T extends Array<any>, R extends Reduction<any, any>> = R[T extends [] ? 0 : 1]
// Tuple reversal
interface ReverseRec<H extends Array<any>, T extends Array<any>>
extends Reduction<H, Reduce<Tail<T>, ReverseRec<Cons<H, Head<T>>, Tail<T>>>> {}
type Reverse<T> = T extends Array<any> ? Reduce<T, ReverseRec<[], T>> : never
// Currying, finally
interface CurryRec<H, T extends Array<any>>
extends Reduction<
H,
Reduce<
Tail<T>,
CurryRec<(arg: Head<T>) => H, Tail<T>>
>
>{}
type Curry<F extends (...args: any[]) => any> = Reverse<Parameters<F>> extends infer R
? R extends any[]
? Reduce<R, CurryRec<ReturnType<F>, R>>
: never
: never
declare function curry<F extends (...args: any[]) => any>(f: F): Curry<F>
declare const f: (a: number, b: string, c: symbol, d: boolean, e: void) => boolean
const curried = curry(f) // (args_0: number) => (args_0: string) => (args_0: symbol) => (args_0: boolean) => (args_0: void) => boolean
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment