[英]Optional property based on value of enum
I'm trying to get some typings to work for a react useReducer
.我正在尝试让一些类型为 react
useReducer
。
Basically I have an action that has an optional property ( data
) based on the value of another property - so if STATUS
is VIEW
or EDIT
, the action must have the data
property.基本上我有一个动作,它有一个基于另一个属性的值的可选属性(
data
) - 所以如果STATUS
是VIEW
或EDIT
,则该动作必须具有data
属性。 I almost have something working, but there's one case (see below) where this fails.我几乎有一些工作,但有一种情况(见下文)失败。
I guess one way of doing this is by explicitly setting STATUS.NEW
to not require the extra property ( { type: 'SET_STATUS'; status: STATUS.NEW }
), but I'm wondering if theres a better way.我想这样做的一种方法是显式设置
STATUS.NEW
不需要额外的属性( { type: 'SET_STATUS'; status: STATUS.NEW }
),但我想知道是否有更好的方法。 If in the future I added a bunch of different statuses then I'd have to specify each one to not require the data property.如果将来我添加了一堆不同的状态,那么我必须指定每个状态以不需要 data 属性。
enum STATUS {
NEW = 'new',
VIEW = 'view',
EDIT = 'edit'
}
/*
if status is 'view', or 'edit', action should also contain
a field called 'data'
*/
type Action =
| { type: 'SET_STATUS'; status: STATUS }
| { type: 'SET_STATUS'; status: STATUS.VIEW | STATUS.EDIT; data: string; }
// example actions
// CORRECT - is valid action
const a1: Action = { type: 'SET_STATUS', status: STATUS.NEW }
// CORRECT - is a valid action
const a2: Action = { type: 'SET_STATUS', status: STATUS.VIEW, data: 'foo' }
// FAILS - should throw an error because `data` property should be required
const a3: Action = { type: 'SET_STATUS', status: STATUS.EDIT }
// CORRECT - should throw error because data is not required if status is new
const a4: Action = { type: 'SET_STATUS', status: STATUS.NEW, data: 'foo' }
And the second part of the question is how I'd incorporate this into a useCallback
below.问题的第二部分是我如何将其合并到下面的
useCallback
中。 I would have thought that useCallback would be able to correctly infer the arguments into the appropriate action type.我原以为 useCallback 能够正确地将参数推断为适当的操作类型。
/*
assume:
const [state, dispatch] = useReducer(stateReducer, initialState)
*/
const setStatus = useCallback(
(payload: Omit<Action, 'type'>) => dispatch({ type: 'SET_STATUS', ...payload }),
[],
)
/*
complains about:
Argument of type '{ status: STATUS.EDIT; data: string; }' is not assignable to parameter of type 'Pick<Action, "status">'.
Object literal may only specify known properties, and 'data' does not exist in type 'Pick<Action, "status">'
*/
setStatus({ status: STATUS.EDIT, data: 'foo' })
You can define a union of statues that require data
, then exclude them in action representing all the others:您可以定义需要
data
的雕像联合,然后在代表所有其他人的行动中排除它们:
enum STATUS {
NEW = 'new',
VIEW = 'view',
EDIT = 'edit'
}
type WithDataStatuses = STATUS.VIEW | STATUS.EDIT;
type Action =
| { type: 'SET_STATUS'; status: Exclude<STATUS, WithDataStatuses> }
| {
type: 'SET_STATUS';
status: WithDataStatuses;
data: string;
}
// now CORRECT - data is required
const a3: Action = { type: 'SET_STATUS', status: STATUS.EDIT }
Answer for second part of question :-)回答问题的第二部分:-)
Assuming that you have defined Actions
as suggested by @Aleksey L., useCallback
can by typed as follows假设您已按照@Aleksey L. 的建议定义了
Actions
, useCallback
可以按如下方式键入useCallback
// This is overloaded function which can take data or not depending of status
interface Callback {
(payload: { status: Exclude<STATUS, WithDataStatuses> }): void;
(payload: { status: WithDataStatuses; data: string; } ): void;
}
const [state, dispatch] = React.useReducer(stateReducer, {})
// Explicitly type useCallback with Callback interface
const setStatus = React.useCallback<Callback>(
(payload) => dispatch({ type: 'SET_STATUS', ...payload }),
[],
)
setStatus({ status: STATUS.EDIT, data: 'foo' })
setStatus({ status: STATUS.NEW })
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.