Как добавить безопасность типов TypeScript к моему действию приведения? - PullRequest
0 голосов
/ 04 января 2019

Я пишу свое приложение на TypeScript и использую 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 . Также переданное значение должно соответствовать правильному типу для выбранного свойства.

Возможно, использование строкового массива для свойства для изменения не является идеальным, но я не знаю лучшего способа нацеливания только на свойства, доступные в AppState.

1 Ответ

0 голосов
/ 04 января 2019

Если вам нужна подпись индекса, то нет способа проверить ключи типа (так как в конце концов подпись индекса будет означать, что класс индексируется любым ключом, поэтому любой ключ будет действительным)

Для проверки того, что строка является ключом типа, мы можем использовать оператор 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 
...