Typescript Преобразование союза в пересечение

enum keyEnum {
    firstKey = 1,
    secKey = 2,
    thirdKey = 3
};

enum firstPropEnum {
    a = 'a',
    b = 'b',
};

enum secPropEnum {
    c = 'c',
    d = 'd',
};

type firstAndSecPropEnum = firstPropEnum | secPropEnum;

type keyPropObj = {
    [keyEnum.firstKey]: { prop: firstPropEnum },
    [keyEnum.secKey]: { prop: secPropEnum },
    [keyEnum.thirdKey]: { prop: firstAndSecPropEnum },
};

type getKeyProp<T extends keyEnum> = keyPropObj[T]['prop'];

type getKeyPropResult1 = getKeyProp<keyEnum.thirdKey | keyEnum.secKey> // Result secPropEnum | firstPropEnum
// Expected Result secPropEnum.
type getKeyPropResult2 = getKeyProp<keyEnum.thirdKey | keyEnum.firstKey> // Result firstPropEnum | secPropEnum
// Expected Result firstPropEnum.
type getKeyPropResult3 = getKeyProp<keyEnum.secKey | keyEnum.firstKey> // Result firstPropEnum | secPropEnum
// Expected Result never;

Поэтому я ожидал получить пересечение, а не союз. Результат должен быть значением, общим для всех результирующих реквизитов. Любая помощь по тому же будет высоко оценена.


person Amol Gupta    schedule 01.03.2019    source источник


Ответы (2)


Вы можете преобразовывать объединения в пересечения с помощью TS2.8 и выше. В вашем случае я бы, вероятно, сделал что-то вроде этого:

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

// use Lookup<T, K> instead of T[K] in cases where the compiler 
//  cannot verify that K is a key of T
type Lookup<T, K> = K extends keyof T ? T[K] : never;

type getKeyProp<T extends keyEnum> = Lookup<UnionToIntersection<keyPropObj[T]>, 'prop'>;

И типы, которые вы хотите, выпадают по желанию:

type getKeyPropResult1 = getKeyProp<keyEnum.thirdKey | keyEnum.secKey> // secPropEnum.
type getKeyPropResult2 = getKeyProp<keyEnum.thirdKey | keyEnum.firstKey> // firstPropEnum.
type getKeyPropResult3 = getKeyProp<keyEnum.secKey | keyEnum.firstKey> // never.

Надеюсь, это поможет. Удачи!

person jcalz    schedule 01.03.2019
comment
Можем ли мы также сделать Lookup<T, K extends keyof T> = T[K] для лучшего понимания? - person Amol Gupta; 02.03.2019

Ну, вы всегда можете сами пересечь типы:

type getKeyPropResult1 = getKeyProp<keyEnum.thirdKey> & getKeyProp<keyEnum.secKey>
type getKeyPropResult2 = getKeyProp<keyEnum.thirdKey> & getKeyProp<keyEnum.firstKey>
type getKeyPropResult3 = getKeyProp<keyEnum.secKey> & getKeyProp<keyEnum.firstKey>

Довольно уродливая альтернатива, которая работает до 6 ключей:

type getKeyPropSingle<T extends keyEnum> = keyPropObj[T]['prop'];
type getKeyProp<
    T1 extends keyEnum | null = null,
    T2 extends keyEnum | null = null,
    T3 extends keyEnum | null = null,
    T4 extends keyEnum | null = null,
    T5 extends keyEnum | null = null,
    T6 extends keyEnum | null = null,
> = (T1 extends keyEnum ? getKeyPropSingle<T1> : unknown) &
    (T2 extends keyEnum ? getKeyPropSingle<T2> : unknown) &
    (T3 extends keyEnum ? getKeyPropSingle<T3> : unknown) &
    (T4 extends keyEnum ? getKeyPropSingle<T4> : unknown) &
    (T5 extends keyEnum ? getKeyPropSingle<T5> : unknown) &
    (T6 extends keyEnum ? getKeyPropSingle<T6> : unknown);

type second = getKeyProp<keyEnum.secKey>;
type getKeyPropResult1 = getKeyProp<keyEnum.thirdKey, keyEnum.secKey>
type getKeyPropResult2 = getKeyProp<keyEnum.thirdKey, keyEnum.firstKey>
type getKeyPropResult3 = getKeyProp<keyEnum.secKey, keyEnum.firstKey>
person H.B.    schedule 01.03.2019
comment
Я искал более масштабируемый подход. Есть ли способ перебрать перечисление и действовать соответственно. - person Amol Gupta; 01.03.2019
comment
@AmolGupta: Вероятно, нет чистого решения, по крайней мере, я не знаю ни одного. Добавлен еще один вариант, который в некоторой степени работает с отсутствием вариативных дженериков. - person H.B.; 01.03.2019
comment
@AmolGupta: Интересно, что это действительно возможно, но, честно говоря, я бы не стал писать такой код и не рекомендовал бы его использовать. Условные типы в сочетании с сопоставленными типами плохо читаемы и трудны для понимания. - person H.B.; 01.03.2019