首页 文章

Typescript函数中的复杂签名

提问于
浏览
2

下面的泛型函数接受一个函数对象,并返回一个具有相同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 回答

  • 2

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

    首先让我们定义映射类型 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错误是不幸的 . 祝你好运!

  • 3

    您可以使用conditional types(在编写本文时尚未发布的2.8版本,在 npm install -g typescript@next 中计划用于March release)来获取此类型的原型2.8 .

    // 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 }
    

相关问题