如何在接口中使用typescript泛型类型? export const createModel=< T R={ [propName:string]:(prevState:T,payload:P)=>T; } >(型号:{ 国家:T; 还原剂:R; 效果:(发送:{ [K in keyof R]:( 有效载荷:R[K]扩展(prevState:T,有效载荷:推断P)=>T?P:从不 )=>T })=>记录承诺>; })=>模型; const model=createModel({ 州:['foo'], 减速器:{ 更新(prevState,有效负载:字符串[]){ 返回[ …国家, …有效载荷 ] } }, 效果:发送=>({ 异步请求(){ dispatch.update(['bar'])) //(有效负载:未知)=>string[] } }) })

如何在接口中使用typescript泛型类型? export const createModel=< T R={ [propName:string]:(prevState:T,payload:P)=>T; } >(型号:{ 国家:T; 还原剂:R; 效果:(发送:{ [K in keyof R]:( 有效载荷:R[K]扩展(prevState:T,有效载荷:推断P)=>T?P:从不 )=>T })=>记录承诺>; })=>模型; const model=createModel({ 州:['foo'], 减速器:{ 更新(prevState,有效负载:字符串[]){ 返回[ …国家, …有效载荷 ] } }, 效果:发送=>({ 异步请求(){ dispatch.update(['bar'])) //(有效负载:未知)=>string[] } }) }),typescript,typescript-generics,Typescript,Typescript Generics,但我得到了: TS2322: Type '<P>(prevState: string[], payload: string[]) => string[]' is not assignable to type '<P>(prevState: string[], payload: P) => string[]' TS2322:Type'(prevState:string[],payload:string[])=>string[] 不可分配给类型“(prevSta

但我得到了:

TS2322: Type '<P>(prevState: string[], payload: string[]) => string[]'
is not assignable to type '<P>(prevState: string[], payload: P) => string[]'
TS2322:Type'

(prevState:string[],payload:string[])=>string[] 不可分配给类型“

(prevState:string[],payload:P)=>string[]”

如何使dispatch的属性函数获得正确的有效负载类型。 可能它的工作方式类似于
Vue.defineComponent()


顺便说一下,任何文章或书籍都可以深入学习打字脚本?

您可能希望
p
T

export const createModel=<
T
R={
[propName:string]:

(prevState:T,payload:P)=>T; //^^^^^^^^^^^添加此 } >(型号:{ // ... })=>模型


这样,无论前一个状态如何,您都会强制下一个状态(
有效负载
)与该类型匹配。

从概念上讲,您的
createModel()
应具有以下类型:

export const createModel = <T, P extends object>(
  model:
    {
      state: T,
      reducers: {
        [K in keyof P]: (prevState: T, payload: P[K]) => T
      },
      effects: (dispatch: {
        [K in keyof P]: (
          payload: P[K]
        ) => T
      }) => Record<string, () => Promise<void>>
    }
) => model;
在这里,类型
T
被正确地推断为
string[]
,但是编译器无法使用
reducer
effects
来推断
p
的键,编译器最终返回到
对象
约束。所以你会犯错误

如果您不介意在调用
createModel()
时手动指定类型参数,那么事情就会解决:

const model = createModel<string[], { update: string[] }>({
  state: ["foo"], reducers: {
    update(prevState, payload) {
      return [
        ...prevState,
        ...payload
      ]
    }
  }, effects: dispatch => ({
    async request() {
      dispatch.update(['bar']) // okay
      // 
    }
  })
});
请注意,从依赖于
p
但不是
p
本身的映射类型的值推断出类似
p
的类型并不总是可能的。从类型
R
本身的值中推断类似
R
的类型要容易得多。这意味着我们必须使用与原始代码类似的方法,从减速器类型计算有效负载类型。这更难看,但有助于推理

无论如何,上面的操作顺序是:首先,我们请求
state
,然后从中推断
T
。然后,我们请求
减缩器
并从中推断
R
,该减缩器已被约束为依赖于已推断的
T
的对象类型。这还应该允许编译器根据上下文推断
prevState
的类型。然后我们要求
effects
,其类型应该已经完全已知,因为
T
R
完全修复了它

让我们试试看:

const model = createModel(["foo"])({
  update(prevState, payload: string[]) {
    return [
      ...prevState,
      ...payload
    ]
  }
})(dispatch => ({
  async request() {
    dispatch.update(['bar'])
  }
}));

/* const model: {
    state: string[];
    reducers: {
        update(prevState: string[], payload: string[]): string[];
    };
    effects: (dispatch: {
        update: (payload: string[]) => string[];
    }) => Record<string, () => Promise<void>>;
} */
const model=createModel([“foo”])({
更新(prevState,有效负载:字符串[]){
返回[
…国家,
…有效载荷
]
}
})(调度=>({
异步请求(){
dispatch.update(['bar']))
}
}));
/*常数模型:{
状态:字符串[];
减速器:{
更新(prevState:string[],有效负载:string[]):string[];
};
效果:(发送:{
更新:(有效载荷:string[])=>string[];
})=>记录承诺>;
} */
看起来不错。编译器推断
T
string[]
R
减缩器的正确类型,然后
effects
也正确键入


手动指定类型是否比编写复杂类型的curry函数更烦人取决于您


你能清楚地说明
createModel
应该做什么吗?您的类型参数
R
可以由任何类型指定(例如
boolean
);你只是给了它一个机会。使用默认值时,它希望
R
是一个包含函数的对象,根据
P
泛型的范围,每个函数都可以处理任何
有效负载。而且它还可以有所有可能的键,因此没有任何东西可以阻止您调用
dispatch.florbegarbet(['bar'])
。我很困惑,我想你可能在寻找类似这样的东西,我基本上被迫将你的函数拆分成一个版本,以便得到你想要的类型推断(在一个函数和一个对象中完成所有操作,最终会与上下文和泛型类型推断中的某些限制相冲突;有关更多信息,请参阅)。如果满足您的需要,我将写一个答案;如果不满足,请详细说明。我想要一个可以推断减缩器函数中的
有效负载类型的函数(实际上,有效负载的类型可能与状态不同)。在编写副作用函数时,我希望获得
dispatch
对象的正确类型(正如您所看到的,它是从reducers对象推断出的映射类型)来帮助我获得正确的类型提示。提前谢谢。@jcalzAnd您看过了吗?这是否满足您的需要?这是我在不需要在多个位置进行手动类型注释的情况下进行推理的唯一方法;如果您的代码需要在有序的事件链中进行推理,这是TypeScript的设计限制(例如,首先从
state
单独推断
T
,然后从
T
单独推断
prevState
,然后从
T
单独推断
R
reducer
,然后在
效果中使用推断出的
R
T
)编译器可能无法在单个函数调用中一次完成所有操作。通过对函数进行curry处理,您可以控制顺序。我可能会对此给出一个答案。
export const createModel = <T,>(state: T) =>
  <R extends Record<keyof R, (prevState: T, payload: any) => T>>(reducers: R) =>
    (effects: (dispatch: {
      [K in keyof R]: (
        payload: R[K] extends (prevState: any, payload: infer P) => any ? P : never
      ) => T
    }) => Record<string, () => Promise<void>>) => ({ state, reducers, effects });
const model = createModel(["foo"])({
  update(prevState, payload: string[]) {
    return [
      ...prevState,
      ...payload
    ]
  }
})(dispatch => ({
  async request() {
    dispatch.update(['bar'])
  }
}));

/* const model: {
    state: string[];
    reducers: {
        update(prevState: string[], payload: string[]): string[];
    };
    effects: (dispatch: {
        update: (payload: string[]) => string[];
    }) => Record<string, () => Promise<void>>;
} */