如何严格键入身份函数作为特定的转换?

时间:2017-11-23 18:45:28

标签: typescript generics transformation

如何定义一个函数类型transform<I, O>,它接受​​类型I的输入并返回类型为O的输出,然后将identity函数定义为transform的特定实现?

例如

type transform<I, O> = (input: I) => O;

const identity = // ...

这样我就可以定义一个函数

const mapToObject = <K, V, O>(
  map: Map<K, V>, 
  transformValue: transform<V, O> = identity
) => Array.from(map.entries()).reduce((obj, [key, val]) => {
  obj[key] = transformValue(value);
  return obj;
}, {});

mapToObject函数应该使用MaptransformValue函数,并将地图转换为简单的JS对象,将transformValue应用于地图中的每个值。 transformValue应默认使用identity函数v => v

身份函数映射anyany有效,但我对严格类型的解决方案感兴趣。

2 个答案:

答案 0 :(得分:1)

只有一种明智的方式来输入身份功能,这里没什么惊喜

const identity = <T>(t: T) => t;

但严格类型的标识函数不能赋予任意变换 - 编译器知道它返回的类型与收到的相同,因此mapToObject的输入不一致 - 当你将其称为<时,结果类型是什么? / p>

   mapToObject<string, number, Date>(new Map<string, number>())
  • 因为它使用默认的身份转换,所以对象必须包含数字
  • 但您明确提供了第三个通用参数,因此生成的对象必须包含Dates

这需要表示为依赖类型 - 结果对象的类型取决于是给定transform参数还是使用default。在TypeScript中,您可以使用overloading function declarations执行此操作:

function mapToObject<K, V>(map: Map<K, V>)
    : { [n: string]: V };
function mapToObject<K, V, O>(map: Map<K, V>, transformValue: transform<V, O>)
    : { [n: string]: O };
function mapToObject<K, V, O>(map: Map<K, V>, transformValue?: transform<V, O>)
    : { [n: string]: {} } 
{
    const transform: (v: V) => {} = transformValue || identity; 
    return Array.from(map.entries()).reduce((obj, [key, val]) => {
        obj[key.toString()] = transform(val) ;
        return obj;
    }, {});
}

由于实际上没有在实现中使用实际值类型,因此它可以作为空对象类型{}给出,它与V和O类型兼容(您可以使用联合类型V | O相反,因为它也兼容,但它更冗长,并且在这里没有购买任何东西)

使用这些声明,编译器会检测到不一致的用法:

// error: Expected 1-2 arguments, but got 1 
mapToObject<string, number, Date>(new Map<string, number>())

mapToObject<string, number>(new Map<string, number>()) // ok
mapToObject<string, number, Date>(new Map<string, number>(), n => new Date(n)) //ok

并按预期推断结果类型:

const o1 = mapToObject(new Map<string, number>())
 // {[n: string]: number}

const o2 = mapToObject(new Map<string, number>(), n => new Date(n))
 //  {[n: string]: Date}

答案 1 :(得分:0)

您可以将通用参数放入箭头函数或常规函数

type transform<I, O> = (input: I) => O;

const identity = <I, O> (input: I) => {
    return null;
}

//Or 
const identity2 = function <I, O> (input: I) {
    return null;
}

const mapToObject = <K extends string, V, O>(
    map: Map<K, V>, 
    transformValue: transform<V, O> = identity
  ) => Array.from(map.entries()).reduce((obj, [key, val]) => {
    obj[key] = transformValue(val);
    return obj;
  }, <{ [index: string]: O }>{});