是否可以在运行时从文本类型的Typescript并集生成值?

是否可以在运行时从文本类型的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:

我使用从graphql sdl模式定义生成TypesScript类型。模式的相关部分从四种
类型定义了一个
联合
,如下所示:

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
也将静态检查

问题是:是否有可能在运行时从文本类型的联合生成值?或者:是否可以在运行时从文本类型的联合生成TypeScript
Enum

如果这两种方法都不可行:有没有更简洁的方法来进行打字检查并确保不留下任何案例

更新 根据@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