打字稿,将类型从对象映射到元组

时间:2020-06-04 10:33:40

标签: typescript typescript-generics

考虑以下界面:

interface Theme {
  color: {
    primary: {
      light: string
      base: string
      dark: string
    }
    secondary: {
      lighter: string
      light: string
      base: string
      dark: string
      darker: string
    }
  }
}

我正在尝试编写一个允许元组的类型,第一个元素映射到colors中的任何键,第二个元素映射到该键下的任何键(即:base)。

即:

['primary', 'light'] ✅ valid
['secondary', 'darker'] ✅ valid
['primary', 'darker'] ? invalid

这是我对tsplayground所做的尝试,我面临的问题是,如果我想允许多个键作为第一个arg传递,那么第二个需要满足所有第一个。有没有办法告诉打字稿使用传递为类型的文字值?

type PickThemeColor<C extends keyof Theme['color'] = keyof Theme['color']> = [
  C,
  keyof Theme['color'][C]
]

// ?? this complains because 'darker' doesnt appear in both 'primary' and 'secondary' keys

const x: PickThemeColor<'primary' | 'secondary'> = ['secondary', 'darker']

2 个答案:

答案 0 :(得分:3)

这里有2个选项,一个是通用选项,很遗憾,您需要指定该选项或一个联合:

// A generic way
type Typle<K extends keyof Theme['color']> = [K, keyof Theme['color'][K]];

const test1: Typle<'primary'> = ['primary', 'light'];
const test2: Typle<'secondary'> = ['secondary', 'darker'];
const test3: Typle<'primary'> = ['primary', 'darker']; // fails

// A union way.
type Typle2 <K = keyof Theme['color']> = K extends keyof Theme['color'] ? [K, keyof Theme['color'][K]] : never;

const test4: Typle2 = ['primary', 'light'];
const test5: Typle2 = ['secondary', 'darker'];
const test6: Typle2 = ['primary', 'darker']; // fails

否则,您需要一个创建函数来避免必需的通用值。

// a helper function way.
const craeteType = <K extends keyof Theme['color']>(v: Typle<K>): Typle<K> => {
  return v;
}

const test7 = craeteType(['primary', 'light']);
const test8 = craeteType(['secondary', 'darker']);
const test9 = craeteType(['primary', 'darker']); // fails

Playground

答案 1 :(得分:1)

实际上,您非常亲密。唯一缺少的是分发颜色键:

type ColorKey = keyof Theme['color'];
type ShadeKey<K extends ColorKey> = keyof Theme['color'][K];

type PickThemeColor<C extends ColorKey> = C extends ColorKey ? [C, ShadeKey<C>] : never;

const x1: PickThemeColor<'primary' | 'secondary'> = ['primary', 'light'] // OK
const x2: PickThemeColor<'primary' | 'secondary'> = ['secondary', 'darker'] // OK
const x3: PickThemeColor<'primary' | 'secondary'> = ['primary', 'darker'] // Error

Playground


提取

ColorKeyShadeKey只是为了简化PickThemeColor(此处无新内容)。区别在于C extends ColorKey部分distributes超过了颜色键的并集。

因此PickThemeColor<'primary'>将产生
["primary", "light" | "base" | "dark"]

然后PickThemeColor<'primary' | 'secondary'>将产生
["primary", ShadeKey<"primary">] | ["secondary", ShadeKey<"secondary">]