【发布时间】:2020-12-24 17:03:02
【问题描述】:
我正在使用 Angular 开发游戏,并尝试将演示与游戏逻辑分离。为了实现这一点,我构建了一个单独的UiController 服务来处理用户交互和演示。每当需要显示某些内容或需要用户操作时,与游戏逻辑相关的服务都会向 UiController 发出请求。
为了尽可能简洁地实现这一点,我正在尝试抽象出与UiController 交互的接口。一种常见的交互是选择,当玩家必须从同一类别的不同选项中选择一个时使用。该交互由UiController 的requestChoice() 方法处理,该方法需要ChoiceRequest 类型的参数。由于有许多不同的类别可供选择,因此该类型必须包含所有这些类别,并且该方法必须知道如何处理所有这些类别。
例如,用户可能需要选择怪物或英雄。我使用文字类型来引用选项中的选项:
type HeroType = 'warrior' | 'rogue' | 'mage';
type MonsterType = 'goblin' | 'demon' | 'dragon';
我想到的构建ChoiceRequest的第一种方法是使用泛型和条件类型:
type ChoiceType = 'hero' | 'monster';
type OptionsSet<T extends ChoiceType> = T extends 'hero'
? HeroType[]
: T extends 'monster'
? MonsterType[]
: never;
interface ChoiceRequest<T extends ChoiceType> {
player: Player;
type: T;
options: OptionsSet<T>;
}
这在构建这样的选择请求时证明是有用的,因为 type 和 options 中的项目的值被正确预测或拒绝:
const request: ChoiceRequest<'monster'> = {
player: player2,
type: 'monster', // OK, any other value wrong
options: ['demon', 'goblin'] // OK, any value not included in MonsterType wrong.
}
但是,当我尝试让 requestChoice() 方法处理不同的情况时,类型推断无法按预期工作:
public requestChoice<T extends ChoiceType>(request: ChoiceRequest<T>) {
switch (request.type) {
case 'a': // OK, but should complain since values can only be 'hero' or 'monster'
...
case 1: // Here it complains, see below (*)
...
...
}
}
(*) 类型“数字”与类型“T”不可比。 'T' 可能是 用可能无关的任意类型实例化 '数字'。
我以前曾多次遇到此问题,但我不完全理解为什么会发生这种情况。我认为它与条件类型有关,所以我尝试了一种不太优雅的第二种方法:
interface ChoiceMap {
hero: HeroType[];
monster: MonsterType[];
}
type ChoiceType = keyof ChoiceMap;
interface ChoiceRequest<T extends ChoiceType> {
player: Player;
type: T;
options: ChoiceMap[T];
}
但是,这种方法与第一种方法完全一样。
使这项工作按预期进行的唯一方法是第三种方法,将ChoiceRequest明确构建为标记联合,不使用泛型或条件类型:
interface MonsterRequest {
player: Player;
type: 'monster';
options: MonsterType[];
}
interface HeroRequest {
player: Player;
type: 'hero';
options: HeroType[];
}
type ChoiceRequest = MonsterRequest | HeroRequest;
问题:为什么第三种方法有效而前两种方法无效?关于类型推断的工作原理,我缺少什么?在这样的场景中是否有其他模式可以实现我所需要的?
【问题讨论】:
-
似乎只是打字稿通用约束的失败:
function requestChoice(request: ChoiceRequest<"monster">)错误的方式与您期望的一样
标签: typescript generics type-inference union-types conditional-types