将 reducer payload 键入对象类型或此类数组
Typing reducer payload as an object type or array of such
我正在使用 useReducer 来维护一组对象类型的状态,并且我有一些操作类型。界面如下所示:
interface MyObject {
id: string;
foo: string;
bar: string;
}
interface Action {
type: 'add' | 'remove' | 'update';
payload?: Partial<MyObject>;
}
reducer函数:
const reducer = (current: MyObject[], action: Action): MyObject[] => {
switch (action.type) {
case 'add':
return [...current, createBaseMyObject()];
case 'remove':
return current.filter((item) => item.id !== action.payload?.id);
case 'update':
return current.map((item) => {
if (item.id === action.payload?.id) {
return { ...item, ...action.payload };
}
return item;
});
default:
return current;
}
};
工作正常,但我还需要有条件地传递一个 MyObject 数组作为批量更新的有效负载。尝试将类型和功能更改为此:
interface ReducerAction {
type: 'add' | 'remove' | 'update';
payload?: Partial<MyObject> | MyObject[];
}
const reducer = (current: MyObject[], action: Action): MyObject[] => {
switch (action.type) {
case 'add':
return [...current, createBaseMyObject()];
case 'remove':
// Property 'id' does not exist on type 'MyObject[]'
return current.filter((item) => item.id !== action.payload?.id);
case 'update':
// With proper typing maybe this should be dedicated case
if (Array.isArray(action.payload)) {
return action.payload;
}
return current.map((item) => {
// Property 'id' does not exist on type 'MyObject[]'
if (item.id === action.payload?.id) {
return { ...item, ...action.payload };
}
return item;
});
default:
return current;
}
};
我试图避免断言或额外的运行时检查来满足 TS。如果创建一个专用案例可以使它更容易,那很好。我只想尽可能地打出最好的类型,最好保持简单。想法?
如果您希望 Typescript 理解某些类型具有特定的有效负载,那么您需要一个联合类型。我会将 'update'
的两个用例分开。 'replace'
似乎是数组负载的更好名称,因为您要用该数组替换整个状态。
type Action = {
type: 'add';
// no payload
} | {
type: 'remove' | 'update';
// partial but with id required
payload: Partial<MyObject> & Pick<MyObject, 'id'>;
} | {
type: 'replace';
// array of objects
payload: MyObject[];
}
const reducer = (current: MyObject[], action: Action): MyObject[] => {
switch (action.type) {
case "add":
return [...current, createBaseMyObject()];
case "remove":
return current.filter((item) => item.id !== action.payload.id);
case "update":
return current.map((item) => {
if (item.id === action.payload.id) {
return { ...item, ...action.payload };
}
return item;
});
case "replace":
return action.payload;
default:
return current;
}
};
我正在使用 useReducer 来维护一组对象类型的状态,并且我有一些操作类型。界面如下所示:
interface MyObject {
id: string;
foo: string;
bar: string;
}
interface Action {
type: 'add' | 'remove' | 'update';
payload?: Partial<MyObject>;
}
reducer函数:
const reducer = (current: MyObject[], action: Action): MyObject[] => {
switch (action.type) {
case 'add':
return [...current, createBaseMyObject()];
case 'remove':
return current.filter((item) => item.id !== action.payload?.id);
case 'update':
return current.map((item) => {
if (item.id === action.payload?.id) {
return { ...item, ...action.payload };
}
return item;
});
default:
return current;
}
};
工作正常,但我还需要有条件地传递一个 MyObject 数组作为批量更新的有效负载。尝试将类型和功能更改为此:
interface ReducerAction {
type: 'add' | 'remove' | 'update';
payload?: Partial<MyObject> | MyObject[];
}
const reducer = (current: MyObject[], action: Action): MyObject[] => {
switch (action.type) {
case 'add':
return [...current, createBaseMyObject()];
case 'remove':
// Property 'id' does not exist on type 'MyObject[]'
return current.filter((item) => item.id !== action.payload?.id);
case 'update':
// With proper typing maybe this should be dedicated case
if (Array.isArray(action.payload)) {
return action.payload;
}
return current.map((item) => {
// Property 'id' does not exist on type 'MyObject[]'
if (item.id === action.payload?.id) {
return { ...item, ...action.payload };
}
return item;
});
default:
return current;
}
};
我试图避免断言或额外的运行时检查来满足 TS。如果创建一个专用案例可以使它更容易,那很好。我只想尽可能地打出最好的类型,最好保持简单。想法?
如果您希望 Typescript 理解某些类型具有特定的有效负载,那么您需要一个联合类型。我会将 'update'
的两个用例分开。 'replace'
似乎是数组负载的更好名称,因为您要用该数组替换整个状态。
type Action = {
type: 'add';
// no payload
} | {
type: 'remove' | 'update';
// partial but with id required
payload: Partial<MyObject> & Pick<MyObject, 'id'>;
} | {
type: 'replace';
// array of objects
payload: MyObject[];
}
const reducer = (current: MyObject[], action: Action): MyObject[] => {
switch (action.type) {
case "add":
return [...current, createBaseMyObject()];
case "remove":
return current.filter((item) => item.id !== action.payload.id);
case "update":
return current.map((item) => {
if (item.id === action.payload.id) {
return { ...item, ...action.payload };
}
return item;
});
case "replace":
return action.payload;
default:
return current;
}
};