首页 > 解决方案 > 如何在打字稿中制作字符串的子类型

问题描述

我使用 typescript 和 react hooks 制作了一个名为 risk 的游戏。游戏在某种地图上进行。所以首先我有设计我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;
}

现在我做了一个减速器功能。对于所有类型的动作,我使用了两个道具namedata. name将始终是 astring并且 data 将是一个类型,具体取决于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}inIMapEditorStatenameinICountry键的字符串。

有什么方法可以创建一个名为的字符串的子类型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};

如果你能帮助我,我将非常感激,如果你知道游戏是什么,请给出你对我的数据结构的看法。

标签: javascripttypescriptreact-hooks

解决方案


如果您希望能够在编译时进行这些检查,则必须列出所有可能的国家/地区名称:

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

推荐阅读