Как сделать подтип строки в машинописи - PullRequest
2 голосов
/ 25 января 2020

Я создал игру под названием риск с использованием машинописи и реакции на хуки. Игра ведется на какой-то карте. Итак, в первую очередь у меня есть дизайн I MapEditor. Состояние редактора карт выглядит как

export interface IMapEditorState {
   mousePos: IPoint;
   countries: {[k: string]: ICountry};
   continents: { [k: string]: IContinent };
}

countries и continents объекты. Интерфейс для страны выглядит так:

//The "name" property and above key will be same in `{[k: string]: ICountry};` will be same
export interface ICountry {
   name: string;
   border: IDot[];
   neighbours: string[];
   completed: boolean;
}

Теперь я делаю функцию редуктора. Для всех типов действий я использовал два реквизита name и data. name всегда будет string, а тип данных будет зависеть от name

type ActionTypes = {name: "removeCountry", data: string} | {name: "addCountry", data: ICountry};
const reducer = (state: IMapEditorState, action: ActionTypes) => {
   ...
}

Теперь рассмотрим первый тип в ActionTypes, который {name: "removeCountry", data: string}. В методе отправки я буду использовать {name: "removeCountry"}, компилятор заставит передать data как string, но это не может быть любая строка, которую я не хочу. Я хочу, чтобы я мог передавать только строку, которая является ключом {[k: string]: ICountry} в IMapEditorState или name в ICountry.

Есть ли способ, которым я мог бы создать подтип строки с именем CountryName и ее использование

export interface IMapEditorState {
   mousePos: IPoint;
   countries: {[k: CountryName]: ICountry};
   continents: { [k: string]: IContinent };
}
export interface ICountry {
   name: CountryName;
   border: IDot[];
   neighbours: string[];
   completed: boolean;
}
type ActionTypes = {name: "removeCountry", data: CountryName} | {name: "addCountry", data: ICountry};

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

1 Ответ

4 голосов
/ 25 января 2020

Если вы хотите выполнить эти проверки во время компиляции, вам нужно составить список всех возможных названий стран:

type CountryName = 'cName1' | 'cName2' | 'cName3';

Или, если вы можете определить начальный объект из во всех возможных странах вы можете объявить его как const (чтобы TS не обобщал свои строки), а затем взять его ключи через keyof:

const initialCountries = {
    cName1: {
        name: 'cName1',
        completed: false
        // ...
    },
    cName2: {
        name: 'cName2',
        completed: false
    },
    cName3: {
        name: 'cName3',
        completed: false
    },
} as const;
type CountryName = keyof typeof initialCountries;

Результат для CountryName равен "cName1" | "cName2" | "cName3".

Затем вы можете определить IMapEditorState, используя приведенное выше CountryName:

export interface ICountry {
    name: CountryName;
    border: IDot[];
    neighbours: string[];
    completed: boolean;
}
export interface IMapEditorState {
    mousePos: IPoint;
    countries: { [k: CountryName]: ICountry };
    continents: { [k: string]: IContinent };
}

И затем скомпилируется следующее:

const initalIMapEditorState: IMapEditorState = {
    countries: initialCountries,
    // ...
};

, а затем Вы можете использовать CountryName везде, где вам нужно:

type ActionTypes = {name: "removeCountry", data: CountryName} | {name: "addCountry", data: ICountry};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...