【问题标题】:TypeScript: generically infer union type member based on a string literal propertyTypeScript:基于字符串文字属性一般推断联合类型成员
【发布时间】:2019-07-03 03:22:38
【问题描述】:

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

我想用通用的方式写一个函数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

但是,我定义 DeviceOf 类型的方式非常冗长,因为它列出了联合中的每个类型:

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

有没有更简洁的方式来定义DeviceOf?我试过这些:

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。

【问题讨论】:

    标签: typescript typescript3.0


    【解决方案1】:

    找到了另一种方法,只使用不带 infer 关键字的条件类型:

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

    基于 Ryan Cavanaugh 的评论:https://github.com/Microsoft/TypeScript/issues/17915#issuecomment-413347828

    【讨论】:

      【解决方案2】:

      知道了。您必须应用“if”两次,一次用于创建infer 类型,第二次用于检查infer 类型是否扩展设备。只有在分支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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-08-24
        • 1970-01-01
        • 1970-01-01
        • 2019-12-10
        • 2021-11-14
        • 2021-10-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多