[英]How to set the type of property based on the parameter passed in typescript?
I am creating a react app and also using typescript.我正在创建一个反应应用程序并使用打字稿。 I want to have a globalState which will have several properties.
我想要一个具有多个属性的 globalState。 I also need to use
useReducer
.我还需要使用
useReducer
。 I create an interface for action
of the reducer
function.我为
reducer
函数的action
创建了一个接口。 The interface takes in one parameter N
which will be name
of action.该接口接受一个参数
N
,它是动作的name
。 Now I want to set the type of data
according to the N
parameter passed.现在我想根据传递的
N
参数设置data
类型。
I have almost created it using ternary operators.我几乎使用三元运算符创建了它。
export interface IGlobalState {
loading: boolean;
userData: IUserData;
}
interface IUserData {
loggedIn: boolean;
name: string;
}
type GlobalStateActionNames = "setLoading" | "setUserData"
export interface IGlobalStateAction<N extends GlobalStateActionNames = GlobalStateActionNames> {
data: N extends "setLoading"
? boolean
: N extends "setUserData"
? IUserData
: any;
name: N;
}
export const GlobalStateReducer = (
state: IGlobalState,
{name, data}: IGlobalStateAction
): IGlobalState => {
switch(name){
case "setLoading": return {...state, loading: data};
default: return state;
}
};
There is only one problem.只有一个问题。 The
data
parameter in the reducer
will extends all the types which could be the possible value of data. reducer
的data
参数将扩展所有可能是数据值的类型。 So when I set loading
to data
it gives error.因此,当我将
loading
设置为data
它会出错。
Type
'boolean | IUserData'
输入
'boolean | IUserData'
'boolean | IUserData'
is not assignable to type'boolean'
'boolean | IUserData'
不可分配给类型'boolean'
I have two questions.我有两个问题。
Note: I know I can solve this by using case "setLoading": return {...state, loading: data as boolean};
注意:我知道我可以通过使用
case "setLoading": return {...state, loading: data as boolean};
来解决这个问题case "setLoading": return {...state, loading: data as boolean};
. . But globalState will have many other props.
但是 globalState 将有许多其他道具。 So I don't want to that for all the values.
所以我不想对所有的价值观都这样。
The issue is your IGlobalStateAction
, to use it as You need you would need to pass N
generic inside, as this is only way to have conditional working.问题是您的
IGlobalStateAction
,要根据需要使用它,您需要在内部传递N
泛型,因为这是有条件工作的唯一方法。 As you cannot determine which action is passed to reducer, N
cannot be passed at this level.由于您无法确定将哪个 action 传递给 reducer,因此无法在此级别传递
N
You can fix that by type guarding this type or changing it to standard union, its maybe less generic, but works without any conditional typyings.您可以通过保护此类型或将其更改为标准联合来解决该问题,它可能不太通用,但无需任何条件类型即可工作。
// pay attention, type is now simple union
export type IGlobalStateAction =
| {
name: "setLoading",
data: boolean
}
| {
name: 'setUserData',
data: IUserData
}
export const GlobalStateReducer = (
state: IGlobalState,
action: IGlobalStateAction
): IGlobalState => {
switch(action.name){
case "setLoading": return {...state, loading: action.data}; // correctly infers bool
default: return state;
}
};
The second solution is, if you want to leave your conditional type - having type guard.第二种解决方案是,如果你想离开你的条件类型 - 有类型保护。 Consider:
考虑:
// here your original type
// type guard narrowing the type
const isActionWithName = <N extends GlobalStateActionNames>
(a: IGlobalStateAction, n: N): a is IGlobalStateAction<N> => a.name === n;
export const GlobalStateReducer = (
state: IGlobalState,
action: IGlobalStateAction
): IGlobalState => {
// using type guard
if (isActionWithName(action, 'setLoading')) {
return {...state, loading: action.data};
}
return state;
};
What is happening here - isActionWithName
function checks the name and puts correct generic into IGlobalStateAction
(this can be spotted here - a is IGlobalStateAction<N>
).这里发生了什么 -
isActionWithName
函数检查名称并将正确的泛型放入IGlobalStateAction
(这可以在这里发现 - a is IGlobalStateAction<N>
)。 At the point of check, we can determine which exactly name it is, and how it impacts the data
type.在检查时,我们可以确定它的确切名称,以及它如何影响
data
类型。
Sorry for how it looks I was doing it from the phone, but you can check out my repo I have there what you ir looking for对不起,我是通过电话做的,但你可以查看我的回购,我有你想要的东西
https://github.com/EnetoJara/-eneto-react-init https://github.com/EnetoJara/-eneto-react-init
export const DO_SOME = ”DO_SOME”;
export type DO_SOME = typeof DO_SOME;
interface AppAction<T,P> {
type: T;
payload?: P;
}
export function actionCreator(someParam; ParamType): AppAction<DO_SOME, ParamType> {
return {
type: DO_SOME,
payload: someParam
}
Also remember you can do something like this on them functions还记得你可以对他们的功能做这样的事情
function nameOfFunction<T, R> (param: T): Promise<R> {
return httpCal(whatever).then((res: R) => red);
T = type R = result Genetics that's how they are call T = 类型 R = 结果遗传学这就是他们的称呼
Then just to use it let's imagine然后只是为了使用它让我们想象一下
const a = nameOfFunction<UserModel, HttpCode> (user).then((res: HttpCode)=>res);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.