如何在打字稿中制作字符串的子类型



我使用打字稿和反应钩子制作了一个名为 risk 的游戏。游戏是在某种地图上进行的。所以首先我有设计我MapEditor.地图编辑器的状态是这样的

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

countriescontinents是对象。国家/地区的界面看起来像

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

现在我做一个减速器功能。对于所有类型的动作,我都使用了两个道具namedataname将始终是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,但它不能是我不想要的任何字符串。我希望我只能传递字符串,这是IMapEditorState{[k: string]: ICountry}的关键或ICountry中的name

有什么方法可以创建名为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};

如果您帮助我,我将不胜感激,如果知道什么是游戏,请对我的数据结构发表您的看法。

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

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"

然后您可以使用上述CountryName定义IMapEditorState

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

Typescript 4.1.5:

就我而言,我需要将这种格式的字符串化日期"YYYY-MM-DD"标记为 ISODate,而不仅仅是字符串。我使用了这种方法,我认为如果有效字符串的数量不仅仅是几个,它比这个答案更有效:

interface ISODateDifferentiator extends String {
[key: string]: unknown;
}
export type ISODate = ISODateDifferentiator & string; 
export const ValidateIsoDate = (date: string): date is ISODate => {
return date.match(/^d{4}-d{2}-d{2}$/) !== null;
} 

执行不安全的任务时,您将获得所需的Type 'string' is not assignable to type 'ISODate'.,但可以使用 typeguard 函数进行转换。您仍然可以获得字符串方法自动完成。

最新更新