programing

결합 유형을 교차 유형으로 변환

lastmemo 2023. 3. 22. 20:39
반응형

결합 유형을 교차 유형으로 변환

유니언 타입을 교차 타입으로 변환하는 방법이 있습니까?

type FunctionUnion = (() => void) | ((p: string) => void)
type FunctionIntersection = (() => void) & ((p: string) => void)

변형을 적용하려고 합니다.FunctionUnion갖기 위해FunctionIntersection

조합이 교차로 진입하길 바라나?분포 조건형이나 조건형으로부터의 추론이 그렇게 할 수 있습니다.(하지만 교차점에서 결합으로 하는 것은 불가능하다고 생각합니다만, 죄송합니다) 여기 사악한 마법이 있습니다:

type UnionToIntersection<U> = 
  (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never

그것은 유니언을 분배한다.U모든 동의자들이 서로 반하는 위치에 있는 새로운 연합체로 다시 포장하는 거죠유형을 교차점으로 추론할 수 있습니다.I핸드북에 기재되어 있는 바와 같이:

마찬가지로, 반변수 위치에 있는 동일한 유형 변수에 대해 여러 후보가 있으면 교차 유형이 유추됩니다.


효과가 있는지 봅시다.

먼저 괄호 안에 넣겠습니다.FunctionUnion그리고.FunctionIntersection왜냐하면 TypeScript는 함수 반환보다 결합/교차를 더 엄격하게 바인드하는 것처럼 보이기 때문입니다.

type FunctionUnion = (() => void) | ((p: string) => void);
type FunctionIntersection = (() => void) & ((p: string) => void);

테스트:

type SynthesizedFunctionIntersection = UnionToIntersection<FunctionUnion>
// inspects as 
// type SynthesizedFunctionIntersection = (() => void) & ((p: string) => void)

맛있겠다!

일반적으로는 주의해 주세요.UnionToIntersection<>는 TypeScript가 실제 결합이라고 생각하는 몇 가지 세부사항을 공개합니다.예를들면,boolean분명히 내부적으로는 로 표현된다.true | false,그렇게

type Weird = UnionToIntersection<string | number | boolean>

된다

type Weird = string & number & true & false

TS3.6+ 에서는, 이 기능이 매우 큰폭으로 삭감됩니다.

type Weird = never

가치관을 갖는다는 것은 불가능하기 때문입니다.string 그리고. number 그리고. true 그리고. false.

또한 여러 유형의 교차로를 원하지만 결합을 반드시 교차로로 변환하지는 않을 때에도 매우 관련된 문제가 있습니다.임시 조합에 의지하지 않고는 교차로로 바로 갈 수 없어요!

문제는 우리가 교차로를 얻고자 하는 타입은 내부에 조합이 있고, 조합도 교차로로 전환될 수 있다는 것이다.구조를 위한 경비원:

// union to intersection converter by @jcalz
// Intersect<{ a: 1 } | { b: 2 }> = { a: 1 } & { b: 2 }
type Intersect<T> = (T extends any ? ((x: T) => 0) : never) extends ((x: infer R) => 0) ? R : never

// get keys of tuple
// TupleKeys<[string, string, string]> = 0 | 1 | 2
type TupleKeys<T extends any[]> = Exclude<keyof T, keyof []>

// apply { foo: ... } to every type in tuple
// Foo<[1, 2]> = { 0: { foo: 1 }, 1: { foo: 2 } }
type Foo<T extends any[]> = {
    [K in TupleKeys<T>]: {foo: T[K]}
}

// get union of field types of an object (another answer by @jcalz again, I guess)
// Values<{ a: string, b: number }> = string | number
type Values<T> = T[keyof T]

// TS won't believe the result will always have a field "foo"
// so we have to check for it with a conditional first
type Unfoo<T> = T extends { foo: any } ? T["foo"] : never

// combine three helpers to get an intersection of all the item types
type IntersectItems<T extends any[]> = Unfoo<Intersect<Values<Foo<T>>>>

type Test = [
    { a: 1 } | { b: 2 },
    { c: 3 },
]

// this is what we wanted
type X = IntersectItems<Test> // { a: 1, c: 3 } | { b: 2, c: 3 }

// this is not what we wanted
type Y = Intersect<Test[number]> // { a: 1, b: 2, c: 3 }

이 예의 실행은 다음과 같습니다.

IntersectItems<[{ a: 1 } | { b: 2 }, { c: 3 }]> =
Unfoo<Intersect<Values<Foo<[{ a: 1 } | { b: 2 }, { c: 3 }]>>>> =
Unfoo<Intersect<Values<{0: { foo: { a: 1 } | { b: 2 } }, 1: { foo: { c: 3 } }}>>> =
Unfoo<Intersect<{ foo: { a: 1 } | { b: 2 } } | { foo: { c: 3 } }>> =
Unfoo<(({ foo: { a: 1 } | { b: 2 } } | { foo: { c: 3 } }) extends any ? ((x: T) => 0) : never) extends ((x: infer R) => 0) ? R : never> =
Unfoo<(({ foo: { a: 1 } | { b: 2 } } extends any ? ((x: T) => 0) : never) | ({ foo: { c: 3 } } extends any ? ((x: T) => 0) : never)) extends ((x: infer R) => 0) ? R : never> =
Unfoo<(((x: { foo: { a: 1 } | { b: 2 } }) => 0) | ((x: { foo: { c: 3 } }) => 0)) extends ((x: infer R) => 0) ? R : never> =
Unfoo<{ foo: { a: 1 } | { b: 2 } } & { foo: { c: 3 } }> =
({ foo: { a: 1 } | { b: 2 } } & { foo: { c: 3 } })["foo"] =
({ a: 1 } | { b: 2 }) & { c: 3 } =
{ a: 1 } & { c: 3 } | { b: 2 } & { c: 3 }

이것이 다른 유용한 기술도 보여주길 바란다.

나는 그가 설명한 부울 문제를 피하기 위해 @jcalz의 답변을 약간 확장했다.

type UnionToIntersectionHelper<U> = (
  U extends unknown ? (k: U) => void : never
) extends (k: infer I) => void
  ? I
  : never;

type UnionToIntersection<U> = boolean extends U
  ? UnionToIntersectionHelper<Exclude<U, boolean>> & boolean
  : UnionToIntersectionHelper<U>;

기본적으로는 이 변환이 방해됩니다.true | false완전히 숨어서true & false보존,boolean그 본질.

이제 정확하게 말할 것이다.UnionToIntersection<boolean>boolean,것은 아니다.never라고 하는 것은 맞지만,UnionToIntersection<boolean | string>never

기발한 라이브러리 유틸리티 타입에서 이용 가능 (2022/11년도 주간 150만건 다운로드)

또는 유사한 유형UnionToIntersection(2022/11년도 주간 다운로드 1억1700만 건) 다른 보석 라이브러리 타입 페스트에서 다운로드.

다음과 같이 사용합니다.

import type { UnionToIntersection } from 'utility-types'
// or
import type { UnionToIntersection } from 'type-fest'

type FunctionUnion = (() => void) | ((p: string) => void)
type FunctionIntersection = UnionToIntersection<FunctionUnion>
// (() => void) & ((p: string) => void)

언급URL : https://stackoverflow.com/questions/50374908/transform-union-type-to-intersection-type

반응형