Проверка на тип соединения - PullRequest
1 голос
/ 05 января 2020

Я ищу способ использовать типы объединения в качестве аргументов функции, а затем иметь возможность использовать аргументы, любые пропущенные аргументы будут неопределенными

Однако здесь имя и возраст вызывают проблемы с типами.

function example(props: { id: number } & ({ name: string } | { age: number })) { 
  const { id, name, age } = props
}

Вот что я хотел бы:

example({ id: 1, name: "Tom" })

example({ id: 1, age: 31 })

Ответы [ 2 ]

1 голос
/ 05 января 2020

Небольшой вариант StrictUnion найден здесь будет хорошо работать:

type UnionKeys<T> = T extends T? keyof T : never;
type StrictUnionHelper<T, TAll> = T extends T? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, undefined>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>

function example(props: StrictUnion<{ id: number } & ({ name: string } | { age: number })>) { 
  const { id, name, age } = props
}

Playground Link

The Way StrictUnion работа заключается в обеспечении всех членов союза всех членов от всех членов союза. Это обеспечивается добавлением любых членов, которые отсутствуют с типом undefined. Так что этот тип: { id: number } & ({ name: string } | { age: number }) станет таким: { id: number; name: string; age: undefined } | { id: number; name: undefined; age: number }. Поскольку этот новый тип имеет ту же структуру, мы можем деструктурировать его.

Чтобы построить StrictUnion, мы должны сначала получить объединение всех ключей от всех составляющих объединения. Для этого мы должны использовать дистрибутивное поведение условных типов. Используя это поведение, мы можем создать тип, который извлекает ключи каждой составляющей объединения и создает объединение всех. Для запуска распределительного поведения мы можем использовать всегда истинное условие (T extends T, T extends unknown или, менее идеальное T extends any). С этим мы получаем следующий тип извлечения всех ключей:

type UnionKeys<T> = T extends T ? keyof T : never;

Ниже мы можем увидеть, как этот тип применяется:

type A = { id: number; name: string }
type B = { id: number; age: number }

UnionKeys<A | B>
  // Conditional type is applied to A and B and the result unioned
  <=> (A extends unknown ? keyof A: never) | (B extends unknown ? keyof B: never) 
  <=> keyof A | keyof B
  <=> ("id" | "name") | ("id" | "age")
  <=> "id" | "name" | "age"

После того, как у нас есть UnionKeys, мы можем использовать другой дистрибутивный условный тип для go через каждого члена объединения и посмотрите, какие ключи отсутствуют в данном типе T (с использованием Exclude<UnionKeys<TAll>, keyof T>) и пересекают исходный T с Partial Record, который содержит эти клавиши набраны как undefined. Нам нужно дважды передать объединение в дистрибутивный тип, один раз для распределения по (T) и один раз, чтобы весь союз мог извлечь ключи, используя UnionKeys.

. посмотрите, как применяется этот тип:

type A = { id: number; name: string }
type B = { id: number; age: number }
StrictUnion<A | B>
  <=> StrictUnionHelper <A | B, A | B>
  // Distributes over T
  <=> (A extends A ? A & Partial<Record<Exclude<UnionKeys<A | B>, keyof A>, undefined>> : never) | (B extends B ? B & Partial<Record<Exclude<UnionKeys<A | B>, keyof B>, undefined>> : never)
  <=> (A extends A ? A & Partial<Record<Exclude<"id" | "name" | "age", "id" | "name">, undefined>> : never) | (B extends B ? B & Partial<Record<Exclude<"id" | "name" | "age", "id" | "age">, undefined>> : never)
  <=> (A extends A ? A & Partial<Record<"age", undefined>> : never) | (B extends B ? B & Partial < Record < "name" >, undefined >> : never)
  // The condition A extends A and B extends B are true and thus the conditional type can be decided
  <=> (A & Partial<Record<"age", undefined>>) | (B & Partial<Record<"name">, undefined>>)
  <=> { id: number; name: string; age?: undefined } | { id: number; age: number; name?: undefined }
0 голосов
/ 05 января 2020

Одним из возможных решений является использование по умолчанию неопределенных аргументов.

function example(props: { id: number } & ({ name: string } | { age: number })) { 
  const available = { ...{ name: undefined, age: undefined }, ...props }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...