【问题标题】:Typescript, map types from object to tuple打字稿,将类型从对象映射到元组
【发布时间】:2023-03-15 09:31:01
【问题描述】:

考虑如下界面:

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 上所做的尝试,我在这里面临的问题是,如果我想允许多个键作为第一个参数传递,那么第二个需要满足所有第一个。有没有办法告诉打字稿使用作为类型传递的文字值?

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']

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    这里有 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

    【讨论】:

    • 谢谢!有一件事,我的智能感知建议第二个参数的所有可能值,而不是将它们限制为第一个参数的键,有没有办法解决这个问题? ?
    • 啊,那么我认为我们需要删除 = keyof Theme['color'] 并始终将其设置为 : Typle&lt;&gt;。我已经更新了答案。
    【解决方案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


    ColorKey 和 ShadeKey 提取出来只是为了简化 PickThemeColor(这里没有什么新东西)。不同之处在于 C extends ColorKey 部分是 distributes 而不是颜色键的联合。

    所以PickThemeColor&lt;'primary'&gt; 会产生
    ["primary", "light" | "base" | "dark"]

    而PickThemeColor&lt;'primary' | 'secondary'&gt; 将产生
    ["primary", ShadeKey&lt;"primary"&gt;] | ["secondary", ShadeKey&lt;"secondary"&gt;]

    【讨论】:

    • 谢谢!这看起来很棒。我仍然看到与上面相同的情况,我对第二个元组值的智能感知总是建议 all 可能的值(而不是基于第一个参数的有效值) - 这可能吗?
    • 不确定智能感知,但类型检查工作如您所见
    猜你喜欢
    • 2018-06-18
    • 1970-01-01
    • 2019-03-22
    • 1970-01-01
    • 2019-11-19
    • 2020-08-06
    • 2021-04-02
    • 2021-01-09
    • 2021-01-26
    相关资源
    最近更新 更多