Skip to content

Instantly share code, notes, and snippets.

@gtkatakura
Last active January 11, 2019 00:38
Show Gist options
  • Save gtkatakura/4f5ec735a4747fb2b9d1f09cf3e5598e to your computer and use it in GitHub Desktop.
Save gtkatakura/4f5ec735a4747fb2b9d1f09cf3e5598e to your computer and use it in GitHub Desktop.
@gtkatakura-bysoft
Copy link

@gtkatakura-bysoft
Copy link

@gtkatakura
Copy link
Author

gtkatakura commented Jan 10, 2019

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'

@gtkatakura
Copy link
Author

gtkatakura commented Jan 11, 2019

@gtkatakura
Copy link
Author

gtkatakura commented Jan 11, 2019

// 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