Проверка машинописи, если тип A === тип B |тип С - PullRequest
4 голосов
/ 07 марта 2019

В одном файле у меня есть что-то вроде этого:

export const _all = {
  a: '',
  b: '',
  c: '',
  d: '',
  e: '',
  f: '',
}
type AllKeysType = typeof _all;
export type AllKey = keyof AllKeysType;

В другом файле у меня есть что-то вроде этого:

export const _keep = {
  a: '',
  b: '',
  d: '',
  e: '',
}
type KeepKeysType = typeof _keep;
export type KeepKey = keyof KeepKeysType;

export const _ignore = {
  c: '',
  f: '',
}
type IgnoreKeysType = typeof _ignore;
export type IgnoreKey = keyof IgnoreKeysType;

Как я могу использовать Typescript, чтобы утверждать, что ключи определеныв _all ВСЕГДА равно объединению _keep и _ignore.Другими словами, AllKey всегда должно быть равно KeepKey |IgnoreKey.

Я хочу, чтобы компилятор Typescript выдавал ошибку, если разработчик обновляет _all, добавляя новое значение (скажем, z), но забывает добавить z в _keep или _ignore.

1 Ответ

6 голосов
/ 07 марта 2019

Это возможно путем определения условного типа, который принимает два типа и разрешается в true, когда типы ввода равны, или false в противном случае. Затем напишите некоторый код, который выдаст ошибку компиляции, если этот тип не true.

Когда один из типов изменится, вы получите ошибку компиляции, которая гарантирует, что вы не забудете обновить тот тип, который не синхронизирован. Это особенно полезно, когда вы хотите получать уведомления об изменениях типа в другой библиотеке.

Например:

type IsExact<T, U> = [T] extends [U] ? [U] extends [T] ? true : false : false;
function assert<T extends true | false>(expectTrue: T) {}

// this will throw a compile error when the two types get out of sync
assert<IsExact<AllKey, KeepKey | IgnoreKey>>(true);

Более надежный код немного длиннее (например, обработка типа any), но он свернут в моей библиотеке здесь .

import { assert, IsExact } from "conditional-type-checks";

// define or import AllKey, KeepKey, IgnoreKey

assert<IsExact<AllKey, KeepKey | IgnoreKey>>(true);

Другой вариант

Еще один не очень хороший способ сделать это - создать два объекта двух типов и назначить их друг другу.

() => {
  let allKeys: AllKey;
  let otherKeys: KeepKey | IgnoreKey;

  // do this in lambdas to prevent the first assignment from changing
  // the type of the variable being assigned to
  () => allKeys = otherKeys;
  () => otherKeys = allKeys;
};
...