如何将身份函数严格键入为特定变换?

How to strictly type an identity function as a specific transform?

我如何定义一个函数类型 transform<I, O> 接受类型 I 的输入和 return 类型 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 有效,尽管我对严格类型化的解决方案很感兴趣。

您可以将泛型参数放入箭头函数或常规函数中

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 }>{});

只有一种合理的方法来输入恒等函数,这并不奇怪

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

但是严格类型的标识函数不能分配给任意转换 - 编译器知道它 returns 与接收到的类型相同,所以你为 mapToObject 键入不一致 - 结果类型是什么当你称它为

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

这需要表示为依赖类型 - 结果对象的类型取决于是给出转换参数还是使用默认值。在 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}