简体   繁体   English

TypeScript:根据字符串文字属性一般推断联合类型成员

[英]TypeScript: generically infer union type member based on a string literal property

TypeScript (v3.2.2) allows me to define a union of interfaces, each with a unique string literal property which can be used as a type guard, eg TypeScript (v3.2.2) 允许我定义接口的联合,每个接口都有一个唯一的字符串文字属性,可以用作类型保护,例如

type Device = Laptop | Desktop | Phone;

interface Laptop {
  type: 'Laptop';
  countDriveBays: number;
  hasTouchScreen: boolean;
}

interface Desktop {
  type: 'Desktop';
  countDriveBays: number;
}

interface Phone {
  type: 'Phone';
  hasTouchScreen: boolean;
}

function printInfo(device: Device) {
  if (device.type === 'Laptop') {
    // device: Laptop
    console.log(
      `A laptop with ${device.countDriveBays} drive bays and ${
        device.hasTouchScreen ? 'a' : 'no'
      } touchscreen.`,
    );
  } else if (device.type === 'Desktop') {
    // device: Desktop
    console.log(`A desktop with ${device.countDriveBays} drive bays.`);
  } else {
    // device: Phone
    console.log(`A phone with ${device.hasTouchScreen ? 'a' : 'no'} touchscreen.`);
  }
}

I want to write a function isDeviceType in a generic way:我想以通用方式编写 function isDeviceType

const isDeviceType = <T extends Device['type']>(type: T) => {
  return (device: Device): device is DeviceOf<T> => device.type === type;
}

// e.g.
const isPhone = isDeviceType('Phone');
isPhone({ type: 'Phone', hasTouchScreen: true }); // true

However, the way I have defined the DeviceOf type is pretty verbose since it lists every single type within the union:但是,我定义DeviceOf类型的方式非常冗长,因为它列出了联合中的每一个类型:

type DeviceOf<Type extends Device['type']> =
  Type extends Laptop['type'] ? Laptop :
  Type extends Desktop['type'] ? Desktop :
  Type extends Phone['type'] ? Phone :
  never;

Is there a more concise way to define DeviceOf ?有没有更简洁的方法来定义DeviceOf I have tried these:我试过这些:

type DeviceOf<Type extends Device['type']> =
  (infer D)['type'] extends Type ? D : never;

// TS2536: Type '"type"' cannot be used to index type 'D'.
// TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.
// TS6133: 'D' is declared but its value is never read.
type DeviceOf<Type extends Device['type']> =
  (infer D) extends Device
    ? D['type'] extends Type
    ? D
    : never
    : never;

// TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.
// TS6133: 'D' is declared but its value is never read.
// TS2304: Cannot find name 'D'.

My impression is that error TS1338 is the limiting factor, and so it's impossible to define DeviceOf in a generic way in the current version of TypeScript.我的印象是错误 TS1338 是限制因素,因此不可能在当前版本的 TypeScript 中以通用方式定义DeviceOf

Got it.知道了。 You have to apply "if" twice, once for create infer type and second to check if infer type extends device.您必须应用“if”两次,一次用于创建infer类型,第二次用于检查infer类型是否扩展设备。 Only in branch D extends Device you will be able u use D['type']只有在分支D extends Device你才能使用D['type']

type DeviceOf<Type extends Device['type']> =
  Device extends (infer D) ?
  D extends Device ?
  D['type'] extends Type ? D : never : never : never;

type Result = DeviceOf<'Laptop'>;

Playground 操场

Found an alternative way, using just conditional types without the infer keyword:找到了另一种方法,只使用没有infer关键字的条件类型:

type FindByType<Union, Type> = Union extends { type: Type } ? Union : never;
type DeviceOf<Type extends Device['type']> = FindByType<Device, Type>;

type Result = DeviceOf<'Laptop'>;

Based on Ryan Cavanaugh's comment here: https://github.com/Microsoft/TypeScript/issues/17915#issuecomment-413347828基于 Ryan Cavanaugh 的评论: https : //github.com/Microsoft/TypeScript/issues/17915#issuecomment-413347828

Typescript 2.8 supports the Extract<Type, Union> utility, which makes this even simpler to implement. Typescript 2.8 支持Extract<Type, Union>实用程序,这使得实现起来更加简单。

type Result = Extract<Device, { type: 'Laptop' }>;

From the Typescript documentation here :来自此处的 Typescript 文档:

Constructs a type by extracting from Type all union members that are assignable to Union .通过从Type中提取可分配给Union的所有联合成员来构造一个类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM