【问题标题】:How to add TypeScript type safety to my redux action?如何为我的 redux 操作添加 TypeScript 类型安全?
【发布时间】:2019-01-04 23:09:50
【问题描述】:

我正在使用 TypeScript 编写我的应用程序,并且我正在使用 Redux 来跟踪我的应用程序状态。我的 redux 存储状态如下所示:

interface AppState {
  readonly grid : IGridSettings;
  readonly selected : ISelectedSettings;
  [key : string] : IGridSettings | ISelectedSettings;
}

interface IGridSettings {
  readonly extents : number;
  readonly isXY : boolean;
  readonly isXZ : boolean;
  readonly isYZ : boolean;
  readonly spacing : number;
  [key : string] : number | boolean;
}

interface ISelectedSettings {
  readonly bodyColor : number;
  readonly colorGlow : number;
  readonly lineColor : number;
  readonly selection : number[] | undefined;
  [key : string] : number | number[] | undefined;
}

我创建了以下操作来更新商店,但是 setPropertyValue 函数并不理想,因为它没有类型安全性!

//How to add type safety to this function?
const setPropertyValue = ( property : string[], value : any ) : SetPropertyValueAction => ( {
  type: 'SET_PROPERTY_VALUE',
  payload: {
    property,
    value,
  }
} );

interface SetPropertyValueAction extends Action {
  type : string;
  payload : {
    property : string[];
    value : any;
  };
}

我可以通过这种方式在我的代码中使用此操作:

setPropertyValue( ['grid', 'extents'], 100 );

这可行,但由于 setPropertyValue 函数中没有类型安全,因此没有什么限制我将无效值输入到函数中,如下所示:

setPropertyValue( ['grid', 'size'], 100 ); //invalid since IGridSettings doesn't have a 'size' property
setPropertyValue( ['grid', 'isXY'], -1 ); //value should be a boolean not a number
setPropertyValue( ['selected', 'bodyColor'], '255' ); //value should be a number not a string

有没有办法重写 setPropertyValue 函数,使其只接受对 AppState 中的属性名称有效的参数。此外,传入的值必须与所选属性的正确类型相对应。

也许使用字符串数组来更改 property 并不理想,但我不知道有更好的方法来仅针对 AppState 中可用的属性。

【问题讨论】:

  • 您的用例是否需要索引签名,或者您是否只是添加它们以允许索引? (例如:[key : string] : IGridSettings | ISelectedSettings;
  • 我需要添加索引签名以消除沿途某处的错误消息。没有它们,TypeScript 代码将无法为我编译。

标签: typescript redux type-safety


【解决方案1】:

如果您需要索引签名,则无法验证该类型的键(因为毕竟索引签名意味着类可被任何键索引,因此任何键都是有效的)

要验证字符串是否是某个类型的键,我们可以使用keyof 运算符,结合一些泛型类型参数(以捕获传入的实际键)

interface AppState {
  readonly grid : IGridSettings;
  readonly selected: ISelectedSettings;
}

interface IGridSettings {
  readonly extents : number;
  readonly isXY : boolean;
  readonly isXZ : boolean;
  readonly isYZ : boolean;
  readonly spacing: number;

}

interface ISelectedSettings {
  readonly bodyColor : number;
  readonly colorGlow : number;
  readonly lineColor : number;
  readonly selection : number[] | undefined;
}

const setPropertyValue = <KApp extends keyof AppState, KAppProp extends keyof AppState[KApp]>( property : [KApp, KAppProp], value : AppState[KApp][KAppProp] ) : SetPropertyValueAction => ( {
  type: 'SET_PROPERTY_VALUE',
  payload: {
    property,
    value,
  }
} );

interface SetPropertyValueAction {
  type : string;
  payload : {
    property : PropertyKey[];
    value : any;
  };
}

setPropertyValue( ['grid', 'extents'], 100 );
setPropertyValue( ['grid', 'size'], 100 ); //err
setPropertyValue( ['grid', 'isXY'], -1 ); //err
setPropertyValue( ['selected', 'bodyColor'], '255' );// err 

【讨论】:

  • 在SetPropertyValueAction接口中,你说property的类型是PropertyKey[]什么是PropertyKey,又是怎样的那定义?
  • 应该 SetPropertyValueActionvalue 也不是 any 吗?
  • PropertyKey 在 lib.d.ts(默认的 ts 库)中定义,它只是任意键(一个键可以是 string | number | symbol
  • @MarekKnows.com 我可能是 .. 但您需要将泛型添加到 SetPropertyValueAction 我可以提供一个示例
  • 谢谢,我试试看编译器是否抱怨我删除了索引签名。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-07
  • 2020-11-15
  • 1970-01-01
  • 2016-05-30
  • 2018-11-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多