TypeScript:通常基于字符串文字属性推断联合类型成员 [英] TypeScript: generically infer union type member based on a string literal property

查看:81
本文介绍了TypeScript:通常基于字符串文字属性推断联合类型成员的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

TypeScript(v3.2.2)允许我定义接口的并集,每个接口都有一个唯一的字符串文字属性,可以用作类型保护,例如

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, e.g.

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.`);
  }
}

我想以通用方式编写函数isDeviceType:

I want to write a function isDeviceType in a generic way:

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

但是,我定义DeviceOf类型的方式非常冗长,因为它列出了并集内的每个单一类型:

However, the way I have defined the DeviceOf type is pretty verbose since it lists every single type within the union:

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

是否有更简洁的方法来定义DeviceOf?我尝试了以下方法:

Is there a more concise way to define 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'.

我的印象是错误TS1338是限制因素,因此不可能在当前版本的TypeScript中以通用方式定义DeviceOf.

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.

推荐答案

知道了.您必须两次应用"if",一次用于创建infer类型,第二次用于检查infer类型是否扩展了设备.只有在分支D extends Device中,您才可以使用D['type']

Got it. You have to apply "if" twice, once for create infer type and second to check if infer type extends device. Only in branch D extends Device you will be able u use 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'>;

查看全文

登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆