TypeScript:将对象类型映射为元组的映射类型

7
请确认是否需要将以下内容翻译为中文:

考虑以下接口:

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上尝试的,我面临的问题是如果我希望允许多个键作为第一个参数传递,那么第二个参数需要满足第一个参数的所有值。有没有一种方法可以告诉typescript使用作为类型传递的文字值?

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个回答

3

您有两个选项,一个是通用选项,不幸的是您需要进行具体说明,另一个是联合选项:

// 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

意思是:这是一个 TypeScript Playground 的网址。

谢谢!有一个问题,我的智能感知建议给出了第二个参数的所有可能值,而不是将其限制为第一个参数的键。有没有解决方法? - Samuel
啊,那我认为我们需要移除= keyof Theme['color']并且总是在: Typle<>中设置它。我已经更新了答案。 - satanTime

2
实际上,你非常接近了。唯一缺少的是分配颜色关键字:
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部分,因为它在颜色键的联合上分布

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

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


谢谢!这看起来很棒。但我仍然看到与上面相同的情况,即我的第二个元组值的智能感知始终建议所有可能的值(而不是基于第一个参数的有效值)- 这可能吗? - Samuel
不确定Intellisense,但是正如您所看到的,类型检查按预期工作。 - Aleksey L.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接