如何在接口中使用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>>;
} */