【问题标题】:Correctly type a return type based on object containing typed functions根据包含类型化函数的对象正确键入返回类型
【发布时间】:2022-01-26 01:17:54
【问题描述】:

我正在尝试编写一个函数,该函数接收某个参数以及从该参数类型到各种返回类型的函数映射。返回类型应该是与函数映射具有相同键的对象,但值应该是将函数应用于参数的结果。

所以基本上它会如下使用:

interface Person {
    name: string;
    age: number;
    friends: number;
}

const bunchOfPeople: Person[] = [
    { name: "Tom", age: 6 , friends: 2 },
    { name: "Dick", age: 16, friends: 12 },
    { name: "Harry", age: 26, friends: 5 },
];

const stats = applyFuncs(bunchOfPeople, {
    count: people => people.length,
    avgFriends: people => people.map(p => p.friends).reduce((a, b) => a + b) / people.length,
    allowedToDrink: people => people.filter(p => p.age >= 21).map(p => p.name),
});

// Expected stats:
// {
//   "count": 3,
//   "avgFriends": 6.333333333333333,
//   "allowedToDrink": ["Harry"],
// } 

我的第一次尝试是使用函数的返回类型来推断输出值类型:

function applyFuncs<T, Fs extends {[K in keyof Fs]: (arg: T) => any}>(arg: T, funcs: Fs) {
  var result = {} as {[K in keyof Fs]: ReturnType<Fs[K]>};
  for (const key of (Object.keys(funcs) as Array<keyof Fs>)) {
    result[key] = funcs[key](arg);
  }
  return result;
}

这按预期工作,但由于某种原因,编译器无法推断函数参数的类型,这意味着people 变量的类型为any。鉴于通用函数签名表明Fs 类型的值是从Tany 的函数,我预计people 会被正确推断为与bunchOfPeople 相同的类型,即Person[] .

TS Playground here.

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    因为 TypeScript 是 staticallystructurally-typed,所以对象可以具有超出对象类型中定义的额外属性。从静态类型系统的角度来看,这使得使用在运行时枚举对象键的方法(如 Object.keys(obj)Object.entries(obj) 等)执行操作是不安全的。

    如果您意识到这一警告并接受如果您在以这种方式使用对象之前不验证对象可能出现的错误,那么您可以使用一些断言来获取您想要的类型:

    TS Playground

    type Fn<
      Params extends unknown[] = any[],
      Result = any,
    > = (...params: Params) => Result;
    
    function mapResults <
      Args extends unknown[],
      T extends Record<PropertyKey, Fn<Args>>
    >(args: Args, fnMap: T): { [K in keyof T]: ReturnType<T[K]> } {
      const results = {} as { [K in keyof T]: ReturnType<T[K]> };
      for (const [name, fn] of Object.entries(fnMap)) {
        results[name as keyof T] = fn(...args);
      }
      return results;
    }
    
    interface Person {
      age: number;
      friends: number;
      name: string;
    }
    
    const people: Person[] = [
      { name: "Tom", age: 6 , friends: 2 },
      { name: "Dick", age: 16, friends: 12 },
      { name: "Harry", age: 26, friends: 5 },
    ];
    
    const stats = mapResults([people], {
      count: people => people.length,
      avgFriends: people => people.map(p => p.friends).reduce((a, b) => a + b) / people.length,
      allowedToDrink: people => people.filter(p => p.age >= 21).map(p => p.name),
    });
    
    console.log(stats);
    

    【讨论】:

      猜你喜欢
      • 2020-09-28
      • 2013-10-26
      • 1970-01-01
      • 1970-01-01
      • 2020-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多