Страницы

пятница, 21 апреля 2017 г.

TypeScript Curry

Начал потихоньку писать файлы объявлений к существующему коду и почти сразу же попал на непростые задачи, такие как, например, сигнатура функции каррирования, по-моему не самый простой вопрос. И вот как я его решал: погуглил, как говорится, в гугле, и получилось... что получилось, не без недостатков, если бы была возможность использовать вложенные дженерики, могло бы получиться лучше, тем не менее в моем случае решает. Вот как-нибудь так:

- node_modules/curry/curry.js:
'use strict';
 
module.exports = (fn) => {
  const f = (fn, length, arr) =>
    (...args) =>
      args.length < length ?
        f(fn, length - args.length, arr.concat(args)) :
        fn(...arr.concat(args));
  return f(fn, fn.length, []);
};

node_modules/curry/curry.d.ts:
declare module "curry" {
  export interface curry1<T1, R> {
    (): curry1<T1, R>;
    (t1: T1): R;
  }
 
  export interface curry2<T1, T2, R> {
    (): curry2<T1, T2, R>;
    (t1: T1): curry1<T2, R>;
    (t1: T1, t2: T2): R;
  }
 
  export interface curry3<T1, T2, T3, R> {
    (): curry3<T1, T2, T3, R>;
    (t1: T1): curry2<T2, T3, R>;
    (t1: T1, t2: T2): curry1<T3, R>;
    (t1: T1, t2: T2, t3: T3): R;
  }
 
  export interface curry4<T1, T2, T3, T4, R> {
    (): curry4<T1, T2, T3, T4, R>;
    (t1: T1): curry3<T2, T3, T4, R>;
    (t1: T1, t2: T2): curry2<T3, T4, R>;
    (t1: T1, t2: T2, t3: T3): curry1<T4, R>;
    (t1: T1, t2: T2, t3: T3, t4: T4): R;
  }
 
  export function curry<T1, R>(fn: (t1: T1) => R): curry1<T1, R>;
  export function curry<T1, T2, R>(fn: (t1: T1, t2: T2) => R): curry2<T1, T2, R>;
  export function curry<T1, T2, T3, R>(fn: (t1: T1, t2: T2, t3: T3) => R): curry3<T1, T2, T3, R>;
  export function curry<T1, T2, T3, T4, R>(fn: (t1: T1, t2: T2, t3: T3, t4: T4) => R): curry4<T1, T2, T3, T4, R>;
  // etc...
 
  export = curry;
}

Допустим у нас есть следующий модуль:
node_modules/concat/concat.js:
'use strict';
 
const concat2 = (a, b) => a + b;
const concat3 = (a, b, c) => a + b + c;
const concat4 = (a, b, c, d) => a + b + c + d;
 
module.exports = {
  concat2,
  concat3,
  concat4
};

-  node_modules/concat/concat.d.ts:
declare module "concat" {
  type stringOrNumber = string | number;
  export function concat2(a: string, b: stringOrNumber): string;
  export function concat3(a: string, b: stringOrNumber, c: stringOrNumber): string;
  export function concat4(a: string, b: stringOrNumber, c: stringOrNumber, d: stringOrNumber): string;
}

И нам нужно каррировать экспортируемые функции:
- curry.js:
'use strict';
 
const { concat2, concat3, concat4 } = require('concat');
const curry = require('curry');
const assert = require('assert');
 
const f2 = curry(concat2);
const f21 = f2('1');
const r2 = f21(2);
const r2x = f2('1', 2);
assert(r2 === concat2('1', 2) && r2 === r2x);
 
const f3 = curry(concat3);
const f31 = f3('1');
const f32 = f31(2);
const r3 = f32(3);
const r3x = f3('1', 2, 3);
assert(r3 === concat3('1', 2, 3) && r3 === r3x);
 
const f4 = curry(concat4);
const f41 = f4('1');
const f42 = f41(2);
const f43 = f42(3);
const r4 = f43(4);
const r4x = f4('1', 2, 3, 4);
assert(r4 === concat4('1', 2, 3, 4) && r4 === r4x);
 
const f40 = f4();
const f401 = f40('1');
const f4010 = f401();
const f40102 = f4010(2);
const f401020 = f40102();
const f4010203 = f401020(3);
const f40102030 = f4010203();
const r40 = f40102030(4);
assert(r40 === r4);

И intellisense справляется со своей задачей:




Но если файл объявления модуля будет выглядеть следующим образом:
declare module "concat" {  
  export function concat2<T>(a: T, b: T): T;
  export function concat3<T>(a: T, b: T, c: T): T;
  export function concat4<T>(a: T, b: T, c: T, d: T): T;
}

То intellisense уже не справится:




А жаль. Надеюсь что это временно. Версия TypeScript 2.2.2.