Выберите одну пару ключ-значение из типа - PullRequest
1 голос
/ 08 июля 2019

У меня есть следующий тип:

type Example = {
  key1: number,
  key2: string
}

и мне нужно создать тип на основе Example типа, чтобы быть одним из key: value пары. Конечно, я ищу общее решение. Вот что должен вернуть этот тип:

type Example2 = { ... };
const a: Example2 = { key3: 'a' } // incorrect
const b: Example2 = { key1: 'a' } // incorrect
const c: Example2 = { key2: 1 } // incorrect
const d: Example2 = { key1: 1 } // correct
const e: Example2 = { key2: 'a' } // correct
const f: Example2 = { key1: 1, key2: 'a' } // incorrect

Я пытался использовать это:

type GetOne<T> = { [P in keyof T]: T[P] };
type Example2 = GetOne<Example>;

, но возвращает все свойства и пример с const f, работающим не так, как ожидалось.

1 Ответ

1 голос
/ 08 июля 2019

Мы создадим объединение всех возможностей:

type Example = {
   key1: number,
   key2: string
}

type PickOne<T> = { [P in keyof T]: Record<P, T[P]> & Partial<Record<Exclude<keyof T, P>, undefined>> }[keyof T]
type Example2 = PickOne<Example>;
const a: Example2 = { key3: 'a' } // incorrect
const b: Example2 = { key1: 'a' } // incorrect
const c: Example2 = { key2: 1 } // incorrect
const d: Example2 = { key1: 1 } // correct
const e: Example2 = { key2: 'a' } // correct
const f: Example2 = { key1: 1, key2: 'a' } // incorrect

То, как мы это делаем, это то, что мы сначала создаем новый тип, который для каждого ключа, у нас есть свойство объекта только с этим ключом (пока игнорируем & Partial<Record<Exclude<keyof T, P>, undefined>>). Так { [P in keyof T]: Record<P, T[P]> } например будет:

type Example2 = {
    key1: Record<"key1", number>;
    key2: Record<"key2", string>;
}

Затем используйте операцию индекса [keyof T], чтобы получить объединение всех значений в этом новом типе, поэтому мы получим Record<"key1", number> | Record<"key2", string>

Этот тип будет работать для всех, кроме последнего теста, где вы хотите запретить использование нескольких свойств из исходного типа. Из-за способа, которым избыточные проверки свойств работают с типами объединения ( см. ), он разрешит ключ, если он присутствует в любом из компонентов объединения.

Чтобы исправить это, мы пересекаем Record<P, T[P]> с типом, который необязательно содержит остальные свойства (Exclude<keyof T, P>), но заставляет их всех быть undefined.

...