Typescript从对象的一个键中推断信息

Typescript从对象的一个键中推断信息,typescript,generics,types,Typescript,Generics,Types,拥有 我试图使这个实现工作,当我尝试使用fn时,在我的b方法中得到类型错误和intelissense。所以基本上fn第一个参数是propOne的关键之一,而fn第二个参数是基于您选择的方法。因为在这个示例中,我选择了带有“methodOne”的fn,所以第二个参数被推断为number,因为methodOne的arg2是number类型 如果我为propOne方法编写类型,我就能够做到这一点,但我想知道是否有更好的方法来实现这一点 除了我认为稍后我们将讨论的bug之外,您可以让它大部分工作 第一个

拥有

我试图使这个实现工作,当我尝试使用fn时,在我的b方法中得到类型错误和intelissense。所以基本上fn第一个参数是propOne的关键之一,而fn第二个参数是基于您选择的方法。因为在这个示例中,我选择了带有“methodOne”的fn,所以第二个参数被推断为number,因为methodOne的arg2是number类型


如果我为propOne方法编写类型,我就能够做到这一点,但我想知道是否有更好的方法来实现这一点

除了我认为稍后我们将讨论的bug之外,您可以让它大部分工作

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

我们将使用两个类型参数,一个用于
propOne
,另一个用于
propTwo
(我将其重命名为
actions
translations
,正如我们在推特上讨论的那样,最初的名称只是Vue商店道具的替代品)

捕获类型参数后,只需将
操作的类型参数转换为函数的合适参数

const a = {
  propOne: {
    methodOne(arg1, arg2: number) {
      console.info('hello...')
    }
  },
  proptwo: {
    b(fn, arg2) {
      fn('methodOne', 'hello') // Error
      fn('methodOne', 1) // OK
    }
  }
}
type Func=(…a:any[])=>void;
类型SkipFirstParameter=T扩展(a:any,…r:inferp)=>any?P:[];
类型操作=(突变:MK,…a:SkipFirstParameter)=>void
函数创建>(o:{
突变:M
行动:A
}) {
返回o
}
常数a=创建({
突变:{
方法一:(arg1:number,arg2:number)=>{
console.info('hello…'))
},
},
行动:{
b:(fn,arg2:数字)=>{
fn(“方法一”,arg2)
fn(“方法一”、“arg2”)
}
}
});


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

嗨,提香,我更新了示例,使其更具体地针对实际问题。如果你愿意,我可以接受最初的回答,而不是问一个与当前问题不同的问题。让我知道
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")
    }
  }
});