admin管理员组文章数量:1415100
I making a game called risk using typescript and react hooks. The game is played on some kind of a map. So first of all I have design I MapEditor
. The state of Map Editor is like
export interface IMapEditorState {
mousePos: IPoint;
countries: {[k: string]: ICountry};
continents: { [k: string]: IContinent };
}
countries
and continents
are objects. The interface for country looks like
//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[];
pleted: boolean;
}
Now I make a reducer function. For all the types of action I used two props name
and data
. name
will always be a string
and data will a type depending on name
type ActionTypes = {name: "removeCountry", data: string} | {name: "addCountry", data: ICountry};
const reducer = (state: IMapEditorState, action: ActionTypes) => {
...
}
Now see the first type in ActionTypes
which is {name: "removeCountry", data: string}
. In the dispatch method a I will use {name: "removeCountry"}
the piler will force to pass data
as string
but it couldn't be any string which I don't want. I want that I should only be able to pass string which is key of {[k: string]: ICountry}
in IMapEditorState
or the name
in ICountry
.
Is there any way that I could create a subtype of a string called CountryName
and use it
export interface IMapEditorState {
mousePos: IPoint;
countries: {[k: CountryName]: ICountry};
continents: { [k: string]: IContinent };
}
export interface ICountry {
name: CountryName;
border: IDot[];
neighbours: string[];
pleted: boolean;
}
type ActionTypes = {name: "removeCountry", data: CountryName} | {name: "addCountry", data: ICountry};
I will very thankful if you help me and kindly give your view on my data structure if got idea what is the game.
I making a game called risk using typescript and react hooks. The game is played on some kind of a map. So first of all I have design I MapEditor
. The state of Map Editor is like
export interface IMapEditorState {
mousePos: IPoint;
countries: {[k: string]: ICountry};
continents: { [k: string]: IContinent };
}
countries
and continents
are objects. The interface for country looks like
//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[];
pleted: boolean;
}
Now I make a reducer function. For all the types of action I used two props name
and data
. name
will always be a string
and data will a type depending on name
type ActionTypes = {name: "removeCountry", data: string} | {name: "addCountry", data: ICountry};
const reducer = (state: IMapEditorState, action: ActionTypes) => {
...
}
Now see the first type in ActionTypes
which is {name: "removeCountry", data: string}
. In the dispatch method a I will use {name: "removeCountry"}
the piler will force to pass data
as string
but it couldn't be any string which I don't want. I want that I should only be able to pass string which is key of {[k: string]: ICountry}
in IMapEditorState
or the name
in ICountry
.
Is there any way that I could create a subtype of a string called CountryName
and use it
export interface IMapEditorState {
mousePos: IPoint;
countries: {[k: CountryName]: ICountry};
continents: { [k: string]: IContinent };
}
export interface ICountry {
name: CountryName;
border: IDot[];
neighbours: string[];
pleted: boolean;
}
type ActionTypes = {name: "removeCountry", data: CountryName} | {name: "addCountry", data: ICountry};
I will very thankful if you help me and kindly give your view on my data structure if got idea what is the game.
Share Improve this question asked Jan 25, 2020 at 7:38 Maheer AliMaheer Ali 36.7k7 gold badges49 silver badges82 bronze badges 4- @CertainPerformance I have created anything further. Just wanted to get a better way to further. – Maheer Ali Commented Jan 25, 2020 at 7:47
-
@CertainPerformance Consider a
function test(x : CountryName){...}
. Now here I want could only pass thename
property ofICountry
(For the time ignore the key of the object) – Maheer Ali Commented Jan 25, 2020 at 7:51 -
Given that the interface is
name: CountryName;
, typingfunction test(x : CountryName)
should already require that the parameter is of the permitted type, aCountryName
and nothing else, right? – CertainPerformance Commented Jan 25, 2020 at 7:59 -
@CertainPerformance I also want the keys of the
countries
to be of typeCountryName
. But if its not possible so please explain why not. – Maheer Ali Commented Jan 25, 2020 at 8:05
2 Answers
Reset to default 5If you want to be able to do these checks at pile time, you'll have to make a list of all possible country names:
type CountryName = 'cName1' | 'cName2' | 'cName3';
Or, if you can define an initial object of all possible countries, you can declare it as const
(so that TS doesn't generalize its strings), and then take its keys via keyof
:
const initialCountries = {
cName1: {
name: 'cName1',
pleted: false
// ...
},
cName2: {
name: 'cName2',
pleted: false
},
cName3: {
name: 'cName3',
pleted: false
},
} as const;
type CountryName = keyof typeof initialCountries;
Result for CountryName
is "cName1" | "cName2" | "cName3"
.
Then you can define IMapEditorState
using the above CountryName
:
export interface ICountry {
name: CountryName;
border: IDot[];
neighbours: string[];
pleted: boolean;
}
export interface IMapEditorState {
mousePos: IPoint;
countries: { [k: CountryName]: ICountry };
continents: { [k: string]: IContinent };
}
And then the following will pile:
const initalIMapEditorState: IMapEditorState = {
countries: initialCountries,
// ...
};
and then you can use CountryName
wherever else you need to:
type ActionTypes = {name: "removeCountry", data: CountryName} | {name: "addCountry", data: ICountry};
Typescript 4.1.5:
In my case I needed to mark a stringified date in this format "YYYY-MM-DD" as an ISODate rather than just a string. I used this approach, I think it's more effective than this answer if the number of valid strings is more than just a few:
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;
}
You get the desired Type 'string' is not assignable to type 'ISODate'.
when doing an unsafe assignment but can convert using the typeguard function. You also still get the string method auto-pletion.
本文标签: javascriptHow to make a subtype of string in typescriptStack Overflow
版权声明:本文标题:javascript - How to make a subtype of string in typescript - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745154962a2645111.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论