Как объединить карту типов в один плоский тип в TypeScript - PullRequest
1 голос
/ 13 октября 2019

Что мне нужно

У меня есть неопределенное количество модов во входном объекте:

const mod1 = {
  actions: {
    a() { },
    b() { },
  }
}

const mod2 = {
  actions: {
    c() { },
    d() { },
  }
}

const input = {
  mods: {
    mod1,
    mod2
  }
}

Во время выполнения библиотека объединяет моды в одном объекте, что эквивалентно:

const output = {
  actions: {
    a() { },
    b() { },
    c() { },
    d() { },
  }
}

И я хотел бы создать тип, который описывал бы этот единственный объект.

Что я пробовал

Входные объекты можно описать так:

interface Input {
  mods: Mods
}

interface Mods {
  [name: string]: Mod
}

interface Mod {
  actions: {
    [name: string]: () => void
  }
}

Тогда я не знаю, как объединить содержимое модов:

interface ToOutput<I extends Input> {
  actions: MergeMods<I["mods"]>
}

type MergeMods<M extends Mods> = // How to merge the content of 'M'?

1 Ответ

1 голос
/ 13 октября 2019

Вот решение:

type Output = ToOutput<(typeof input)["mods"]>

interface ToOutput<I extends Mods> {
  actions: UnionToIntersection<I[keyof I]["actions"]>
}

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

Пояснения

Следующий тип:

interface ToOutput<I extends Input> {
    actions: UnionToIntersection<I["mods"][keyof I["mods"]]["actions"]>
}

первое использование keyof и поиск для сопоставления типа Input в соответствии с выходной структурой

interface ToOutputStep1<I extends Input> {
    actions: I["mods"][keyof I["mods"]]["actions"]
}

/*
type T1 = {
    actions:
      | { a: {}; b: {}; }
      | { c: {}; d: {}; };
}
*/
type T1 = ToOutputStep1<typeof input>

, а затем преобразует тип объединения actions в тип пересечения .

/*
type T2 = {
    actions: {
        a: {};
        b: {};
        c: {};
        d: {};
    };
}
*/

type T2 = ToOutput<typeof input>

Детская площадка

...