首页 > 解决方案 > 打字稿在其键之一中从对象推断信息

问题描述

const a = {
  propOne: {
    methodOne(arg1, arg2: number) {
      console.info('hello...')
    }
  },
  proptwo: {
    b(fn, arg2) {
      fn('methodOne', 'hello') // Error
      fn('methodOne', 1) // OK
    }
  }
}

我试图在我的 b 方法中尝试使用 fn 时获得类型 Error 和 Intelisense 的情况下使这个实现工作。所以基本上 fn 第一个参数是 propOne 的键之一,而 fn 第二个参数是基于你选择的方法。由于在示例中我选择了带有“methodOne”的 fn,因此第二个参数被推断为 number,因为 methodOne 的 arg2 是 number 类型。

如果我为 propOne 方法编写类型,我就能完成这项工作,但我想知道是否有更好的方法来解决这个问题。

标签: typescriptgenericstypes

解决方案


你可以让它大部分工作,除了我认为我们稍后会解决的错误。

第一个问题是我们需要使用一个函数来帮助进行推理。简单变量的类型可以从初始化表达式推断或在类型注释中指定,对于更复杂的行为,我们需要使用函数的推断行为。

我们将使用两个类型参数,一个 forpropOne和一个 for propTwo(我已将其重命名为actionsmutations正如我们在 twitter 上讨论的那样,原来的名称只是 Vue store props 的替身)

一旦我们捕获了类型参数,只需将类型参数actions转换为函数的合适参数即可

type Func = (...a: any[]) => void;
type SkipFirstParameter<T extends Func> = T extends (a: any, ... r: infer P) => any ? P : [];
type Actions<M extends Record<string, Func>> = <MK extends keyof M>(mutation: MK, ...a: SkipFirstParameter<M[MK]>) => void

function create<T, M extends Record<string, Func>, A extends Record<string, (f: Actions<M>, ...a: any[]) => void>>(o: {    
  mutations: M
  actions: A
}) {
  return o
}
const a = create({
  mutations: {
    methodOne: (arg1: number, arg2: number) => {
      console.info('hello...')
    },
  },
  actions: {
    b: (fn, arg2: number)  => {
      fn("methodOne", arg2)
      fn("methodOne", "arg2")
    }
  }
});

游乐场链接

你会注意到我切换到箭头函数,而不是速记语法或function表达式。这是因为推理在箭头函数和其他函数之间的行为不同。我相信这是一个编译器错误和字段之一但如果我们使用函数语法的方法M将被推断为unknown. 这可能与 TS 尝试推断this此类函数的方式有关,但行为上的差异可能是一个错误。


推荐阅读