개발하자

TypeScript 매핑된 유형의 역을 얻으려면 어떻게 해야 합니까?

Cuire 2023. 1. 10. 14:12
반응형

TypeScript 매핑된 유형의 역을 얻으려면 어떻게 해야 합니까?

나는 TypeScript 맵핑 타입의 "역"을 찾고 있다. (그 속성은 엄격하게 문자열이므로 "불가역"이다.). 내가 원하는 결과를 설명하기 위해, 나는 일반적인 것이 필요하다

type Inverse<M> = ...

변신할 수 있다

type MappedType = {
  key1: 'value1'
  key2: 'value2'
};

안으로

/**
 * {
 *   value1: 'key1';
 *   value2: 'key2';
 * }
 */
type MappedTypeInverse = Inverse<MappedType>

벌써 몇 가지 시도를 해봤는데.. 그러나 소용이 없었다:

type Inverse<M> = M extends Record<infer O, infer T> ? Record<T, O> : never;

/**
 * type MappedTypeInverse = {
 *   value1: 'key1' | 'key2'
 *   value2: 'key2' | 'key2'
 * }
 */
type MappedTypeInverse = Inverse<MappedType>
type InverseValue<M extends Record<any, any>, V extends M[keyof M]> = V extends M[infer K] ? K : never;

/**
 * type MappedTypeInverseValue = unknown // expecting 'key1'
 */
type MappedTypeInverseValue = InverseValue<MappedType, 'value1'>

이게 가능하기나 해요? 어떤 도움이라도 감사하겠습니다!




이를 달성할 수 있는 방법은 다음과 같습니다. 이 과정의 중간 단계에서 조합을 교차로로 변환하기 위해 에서 일부 "악마술"을 차용한다:

type MappedType = {
    key1: 'value1';
    key2: 'value2';
};

type Intermediate<R extends Record<string, string>> =
    R extends Record<infer K, string>
    ? { [P in K]: { [Q in R[P]]: P; }; }
    : never;

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

type Inverse<R extends Record<string, string>> =
    Intermediate<R> extends Record<string, infer T>
    ? { [K in keyof UnionToIntersection<T>]: UnionToIntersection<T>[K]; }
    : never;

type InverseMappedType = Inverse<MappedType>;
// type InverseMappedType = {
//     value1: 'key1';
//     value2: 'key2';
// }

이 방법의 또 다른 이점은 입력 레코드에 중복된 속성 값이 포함되어 있을 때 적절한 속성 값을 가진 매핑된 유형을 출력한다는 것입니다:

type MappedType = {
    key1: 'value1';
    key2: 'value1' | 'value2';
};

type InverseMappedType = Inverse<MappedType>;
// type InverseMappedType = {
//     value1: never;
//     value2: 'key2';
// }

TypeScript에 나보다 더 정통한 사람은 매핑된 유형을 반전시키는 이것보다 더 짧은 방법을 알고 있을 수 있지만, 이것은 적어도 그 일을 해내는 것처럼 보인다.




다음은 (패트릭 로버트의 좋은 해결책 외에도) 기울어진 부분입니다:

type KeyFromVal<T, V> = {
  [K in keyof T]: V extends T[K] ? K : never
}[keyof T];

// we assume the type to be an object literal with string values
// , should also work with number or symbol
type Inverse<M extends Record<string, string>> = {
  [K in M[keyof M]]: KeyFromVal<M, K>
};

type MappedType = {
  key1: 'value1'
  key2: 'value2'
};

type MappedTypeInverse = Inverse<MappedType> // { value1: "key1"; value2: "key2"; }



연산자와 함께 매핑된 유형을 사용할 수 있습니다.

놀이터의 예를 참조하십시오.

type Inverse<T> = {[K in keyof T as (T[K] & (string | number))]: K};

반응형