TypeScript: обычно выводит член типа объединения на основе свойства строкового литерала - PullRequest
0 голосов
/ 08 февраля 2019

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 является ограничивающим фактором, и поэтомуневозможно определить DeviceOf в общем виде в текущей версии TypeScript.

1 Ответ

0 голосов
/ 09 февраля 2019

Понял.Вы должны применить «если» дважды, один раз для создания типа 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'>;

Детская площадка

...