是否可以在运行时从文本类型的Typescript并集生成值?
我使用从graphql sdl模式定义生成TypesScript类型。模式的相关部分从四种是否可以在运行时从文本类型的Typescript并集生成值?,typescript,graphql,code-generation,Typescript,Graphql,Code Generation,我使用从graphql sdl模式定义生成TypesScript类型。模式的相关部分从四种类型定义了一个联合,如下所示: union Game = GameLobby | GamePlaying | GameOverWin | GameOverTie type GameLobby { id: ID! } type GamePlaying { id: ID! player1:String! player2:String! } type GameOverWin { id:
类型定义了一个联合
,如下所示:
union Game = GameLobby | GamePlaying | GameOverWin | GameOverTie
type GameLobby {
id: ID!
}
type GamePlaying {
id: ID!
player1:String!
player2:String!
}
type GameOverWin {
id: ID!
winner:String!
}
type GameOverTie {
id: ID!
}
并生成以下类型脚本类型定义:
export-type Game=GameLobby | GamePlaying | GameOverWin | GameOverTie;
导出类型={
__类型名称?:“游戏大厅”;
只读id:标量[“id”];
};
导出类型GameOverTie={
__typename?:“GameOverTie”;
只读id:标量[“id”];
};
导出类型GAMEOVIN={
__typename?:“GameOverWin”;
只读id:标量[“id”];
只读优胜者:字符串;
};
导出类型游戏性={
__类型名称?:“游戏性”;
只读播放器1:字符串;
只读播放器2:字符串;
};
现在,我希望能够在运行时使用类型联合来区分游戏当前的状态。我可以这样定义这种联合:
//假设这会返回生成的类型:
从“/generated/models”导入{Game};
//我们只想要真正的区别对待
类型GameStatus=Exclude;
使用此类型,我可以严格键入可能需要GameStatus
的任何值,例如:
类游戏模型{
公共只读id!:编号;
公共只读状态!:游戏状态;
}
最后,我希望能够将游戏状态映射到持久化状态,为此,我需要枚举GameStatus
实际可以获取的所有可能的值。为了做到这一点,理想情况下,我不希望必须重新键入值,但如果必须,我希望至少确保没有遗漏任何值
现在,这就是我如何确保我涵盖了GameStatus
可以获取的所有可能值:
函数assertNever(值:never):never{
抛出新错误(`unexpected value${value}`);
}
导出常量GameModelLobble:GameModelState=“GameLobble”;
导出常量GameModelPlaying:GameModelState=“GamePlaying”;
导出常量gamemodelowin:GameModelState=“GameOverWin”;
导出常量GameModelOverTie:GameModelState=“GameOverTie”;
常量游戏状态=[
游戏模型大厅,
游戏模式,
甘美因,
博弈论
];
//确保我们没有忘记任何状态
gameStatus.forEach(状态=>{
开关(状态){
案例大厅:
打破
案例游戏模式:
打破
案例1:
打破
案例:
打破
违约:
资产从未(状态);
}
});
这使得tsc
检查底层GraphQL模式更改时是否覆盖或删除了所有值。有点像运行时/静态检查的混合,因为我将代码留在运行时执行,但是tsc
也将静态检查
问题是:是否有可能在运行时从文本类型的联合生成值?或者:是否可以在运行时从文本类型的联合生成TypeScriptEnum
如果这两种方法都不可行:有没有更简洁的方法来进行打字检查并确保不留下任何案例
更新
根据@dezfowler的回答,并做了一些小改动,以下是我解决问题的方法:
首先从游戏状态
联合类型中提取鉴别器类型:
从“/generated/models”导入{GameState};
导出类型GameStateKind=排除;
然后构建一个映射类型(这是一种重言式),并以类型安全的方式将类型映射到值。该映射强制您使用所有类型作为键,并写入所有值,因此,除非每个鉴别器都存在,否则它不会编译:
export const StateKindMap:{[k in GameStateKind]:k}={
游戏大厅:“游戏大厅”,
GameOverTie:“GameOverTie”,
GameOverWin:“GameOverWin”,
游戏性:“游戏性”
};
将所有类型导出为数组,然后我可以使用该数组在数据库模型中创建枚举:
export const AllStateKinds = Object.values(StateKindMap);
最后,我编写了一个小测试,以确保我可以直接使用StateKindMap
来区分GameStateKind
(此测试是多余的,因为所有必需的检查都是由tsc
完成的):
从“/model”导入{StateKindMap,AllStateKinds};
描述(“StateKindMap”,()=>{
它(“应该列举所有的州类别”,()=>{
AllStateKinds.forEach(种类=>{
开关(种类){
case StateKindMap.GameLobble:
打破
case StateKindMap.GameOverTie:
打破
case StateKindMap.GameOverWin:
打破
case StateKindMap.GamePlaying:
打破
违约:
资产(实物);
}
});
});
});
函数assertNever(值:never):never{
抛出新错误(`unexpected value${value}`);
}
我意识到我忘了回答你关于在运行时生成内容的问题的第一部分。这是不可能的,因为JavaScript代码中没有TypeScript类型系统的表示。解决方法是创建一个虚拟对象(使用下面的映射类型技术),强制您为所有联合值添加键,以便对其进行编译。然后只需传递该虚拟对象即可获得游戏状态字符串值数组
至于问题的第二部分,关于更简洁的类型检查
你可以使用一个
type StrategyMap={[K in game status]:T};
然后你可以像这样使用它
const title:StrategyMap={
游戏大厅:“你在大厅里”,
GameOverTie:“这是一场平局”,
加梅文:“祝贺你”,
游戏:“游戏开始!”
};
或者这个
const didYouWin=false;
const nextstatus决策:战略地图游戏状态>={
游戏大厅:()=>“游戏性”,
G