如何根据泛型属性 Select 类型?

enum AnimalType {
  DOG,
  CAT,
  FROG,
  SNAKE,
}

const dog = {
  type: AnimalType.DOG, // type property exists for all animals. other properties are vary for different animals
  legs: 4,
}

const cat = {
  type: AnimalType.CAT,
  tail: 1,
}

const frog = {
  type: AnimalType.FROG,
  canSwim: true,
  tail: 0,
}

const snake = {
  type: AnimalType.SNAKE,
  tail: 'looong',
  age: 2,
}

type Animal<AnimalType> = typeof dog | typeof cat | typeof frog | typeof snake // how to rewrite this type definition?

如何根据AnimalType推断类型?例如,Animal<AnimalType.DOG>应该等于typeof dog

对于条件类型,这是可能的:

type Animal<T extends AnimalType> = T extends AnimalType.DOG ? typeof dog : T extends AnimalType.CAT ? typeof cat : T extends AnimalType.FROG ? typeof frog : T extends AnimalType.SNAKE ? typeof snake : unknown

但它看起来太长了,完全无法阅读.我希望有一个更好的解决方案

推荐答案

你很可能是有意

const dog = {
  type: AnimalType.DOG,
  legs: 4,
}

{type: AnimalType.DOG, legs: number}型. 但是TypeScript将AnimalType.DOG的类型推断为更宽的enum类型AnimalType,而不是literal type AnimalType.DOG.

这意味着有人可以写dog.type = AnimalType.CAT,这不是很好.这也意味着typeof dog没有可以用来计算Animal<T>的信息.

要解决这个问题,您需要告诉编译器type应该被赋予一个更具体的文本类型.通常的方法是在有问题的字面上使用const assertion.如果您希望编译器强制要求dog.legs始终为4,则可以对完整的对象文字{type: AnimalType.DOG, legs: 4} as const执行此操作.但至少,我们应该为type号房产这么做.产生这样的结果:

const dog = { type: AnimalType.DOG as const, legs: 4 };
const cat = { type: AnimalType.CAT as const, tail: 1 };
const frog = { type: AnimalType.FROG as const, canSwim: true, tail: 0 };
const snake = { type: AnimalType.SNAKE as const, tail: 'looong', age: 2 };

现在,union type-typeof dog | typeof cat | typeof frog | typeof snake看起来像是:

type SomeAnimal = typeof dog | typeof cat | typeof frog | typeof snake;
/* type SomeAnimal = {
    type: AnimalType.DOG;
    legs: number;
} | {
    type: AnimalType.CAT;
    tail: number;
} | {
    type: AnimalType.FROG;
    canSwim: boolean;
    tail: number;
} | {
    type: AnimalType.SNAKE;
    tail: string;
    age: number;
} */

它允许我们通过联合过滤Extract utility type实现Animal:

type Animal<T extends AnimalType> =
  Extract<SomeAnimal, { type: T }>;

让我们测试一下:

type Dog = Animal<AnimalType.DOG>;
/* type Dog = {
    type: AnimalType.DOG;
    legs: number;
} */

看上go 不错.

Playground link to code

Typescript相关问答推荐

返回对象的TypScript构造函数隐式地替换This的值来替换super(.)的任何调用者?

更新:Typescript实用程序函数合并对象键路径

typescript抱怨值可以是未定义的,尽管类型没有定义

在NextJS中获得Spotify Oauth,但不工作'

迁移到Reaction路由V6时,获取模块Reaction-Router-Dom';没有导出的成员RouteComponentProps错误

在函数中打字推断记录的关键字

获取一个TypeScrip对象,并将每个属性转换为独立对象的联合

剧作家元素发现,但继续显示为隐藏

找不到财产的标准方式?

缩小对象具有某一类型的任意字段的范围

对有效枚举值的常量字符串进行常规判断

垫子工具栏不起作用的Angular 布线

使用强制转换编写打字函数的惯用方法

在TS泛型和记录类型映射方面有问题

带动态键的 TS 功能

为什么我的 Typescript 函数中缺少 void 隐式返回类型?

TS 条件类型无法识别条件参数

如何为对象创建类型,其中键是只读且动态的,但值是固定类型?

从平面配置推断嵌套递归类型

从接口推断模板参数?