简体   繁体   English

如何根据typescript中传入的参数设置属性的类型?

[英]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. reducerdata参数将扩展所有可能是数据值的类型。 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.我有两个问题。

  • How could I resolve this error?我该如何解决这个错误?
  • Is there any better to achieve the above stated functionality?有没有更好的方法来实现上述功能?

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.您可以通过保护此类型或将其更改为标准联合来解决该问题,它可能不太通用,但无需任何条件类型即可工作。

Using union types instead of conditional使用联合类型而不是条件

// 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;
    }
}; 


Using conditional type with type guard使用带有类型保护的条件类型

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM