如何正确键入此对象转换?

How can I correctly type this object converting?

如何实现此转换案例的类型安全?我正在映射 palette 并将任何带有条目的对象转换为键值对,就像 tailwindcss 对其颜色配置所做的那样。但是 colors 的类型不包含灰色,而只包含未转换的输入键作为类型。

const palette = {
    white: 'white',
    gray: {
        100: '#eeeeee',
        200: '#e0e0e0',
        300: '#bbbbbb',
    },
};

type ColorVariants = keyof typeof palette;

function convertToColors(color: ColorVariants) {
    return Object.fromEntries(
        Object.entries(palette[color]).map(([key, value]) => [`${color}${key}`, value]),
    );
}


/**
  result -> 
  {
    "gray100": "#eeeeee",
    "gray200": "#e0e0e0",
    "gray300": "#bbbbbb",
  }
 */
const grays = convertToColors("gray")

这里的类型不正确

// Resulting Type
// const colors: {
//    white: string;
// }
//
// Expecting Type
// const colors: {
//    white: string;
//    gray100: string;
//    gray200: string;
//    gray300: string;
// }
const colors = {
   white: palette.white,
   ...convertToColors("gray")
}

你可以在这种情况下使用 union :

type ColorVariants = string | {[prop:number]:string};

您现在可以 template literal types.

让我们将 convertToColors 编辑为:

function convertToColors<Color extends ColorVariants>(color: Color): Variants<Color, typeof palette[Color]> {
    return Object.fromEntries(
        Object.entries(palette[color]).map(([key, value]) => [`${color}${key}`, value]),
    ) as never; // cast to return type; "ignore" error
}

它接受一种颜色,并为我们提供该颜色的变体对象。

这是建议的Variants类型:

type Variants<K extends string, T extends string | Record<string | number, string>> = T extends string ? T : {
    [V in keyof T as V extends string | number ? `${K}${V}` : never]: T[V];
};

首先它会检查给定的颜色是否没有任何变体(它只是一个字符串,而不是一个对象)。 然后它将变体中的所有键重新映射为变体 + 它的强度。

你可以看到它运行得非常好here