Typescript函数中的复杂签名

时间:2018-02-22 23:19:17

标签: typescript typescript-typings

以下泛型函数接受一个函数对象,并返回一个具有相同propnames的对象,该对象的值与相应函数的返回值一致。

const combineValues = obj => { 
      const res = {};
      for (const k in obj){
        res[k] = obj[k]()
      }
      return res;
    } 
const combined = combineValues({
  n: () => 1,
  s: () => 's'
}); // { n: 1, s: 's' }

尝试在Typescript

中为此函数定义签名和实现
const combineValues =
  <K extends string> (obj: Record<K, () => any>): Record < K, any > => {
    const res = {} as Record<K, any>;
    for (const k in obj){
      res[k] = obj[k]()
    }
    return res;
  }

const combined = combineValues({
  n, s
}) // Record<"n" | "s", any>

combined不保留值的原始输入

const combcombineValues = <K extends string, T>(obj: Record<K, () => T>): Record<K, T> => {
    const res = {} as Record<K, T>;
    for (const k in obj){
      res[k] = obj[k]()
    }
    return res;
  }
仅当所有函数道具返回相同类型T

时,

才有效

是否可以在Typescript中完全定义它?

2 个答案:

答案 0 :(得分:3)

这可以在当前版本的TypeScript(v2.7)中完成而无需等待条件类型,方法是使用inference from mapped types,这是TypeScript的一个功能,其中函数可以推断类似于#34的类型;向后&#34;方向,从输出到输入。

首先让我们定义映射类型Funcs<T>,它接受​​一个普通对象并将其转换为一个对象,其属性是返回普通对象的属性类型的所有函数:

type Funcs<T> = { [K in keyof T]: (...args:any[]) => T[K] }

请注意此类型基本上是您想要做的事情的后退。现在让我们输入combineValues()

const combineValues = <T>(obj: Funcs<T>): T => {
    const res = {} as T;  // only change in body
    for (const k in obj) {
        res[k] = obj[k]()
    }
    return res;
}
如您所见,

Funcs<T>作为输入,并返回T作为输出。让我们看看它是否有效:

const combined = combineValues({
    n: () => 1,
    s: () => 's'
}); // { n: number, s: string }

几乎你想要什么。唯一的区别是TypeScript倾向于将() => 1解释为返回number的函数,而不是返回文字1的函数。你可以做些什么来解决这个问题;最简单的(虽然有点重复)是断言返回类型文字:

const combined = combineValues({
    n: () => 1 as 1,
    s: () => 's' as 's'
}); // { n: 1, s: 's' }

希望有所帮助。祝你好运!

更新:注意到编译器似乎在TypeScript 2.7中将combined键入为{n: any, s: any}。幸运的是,当您使用Intellisense检查类型时,似乎只会发生这种情况,正如Microsoft/TypeScript#14041中提到的那样(我认为)。如果您实际使用该值,您将看到它具有正确的类型:

combined.n = 0 // error, 0 is not assignable to 1
combined.s = 0 // error, 0 is not assignable to 's'

Playground link

所以,我想我会坚持这个答案,虽然Intellisense错误是不幸的。祝你好运!

答案 1 :(得分:2)

您可以使用conditional types在打字稿2.8中实现此目的(在撰写本文时尚未发布2.8,您可以通过npm install -g typescript@next计划March release)。

// ReturnType<T> below is from 2.8 and is a conditional type
type AllReturnTypes<T extends { [name: string]: (...args: any[]) => any }> = { [P in keyof T]: ReturnType<T[P]> }
const combineValues = <T extends { [name: string]: () => any }>(obj: T): AllReturnTypes<T> => {
    const res = {} as AllReturnTypes<T>;
    for (const k in obj) {
        res[k] = obj[k]()
    }
    return res;
}

const combined = combineValues({
    n: () => 1, s: () => ""
}) // will be of type { n: number, s: string }