我正在通过重新创建一个类似于CVA库的CSS类构建器函数来自学如何使用typescript泛型。然而,我被如何从泛型中提取键所困扰。我有以下代码(为了简洁而简化):
type TConfig = {
base?: string;
variants?: Record<string, Record<string, string>>;
};
type TInput<T extends TConfig> = Record<keyof T["variants"], string>;
export const classBuilder = <T extends TConfig>(config: T) => {
///...
return (input: TInput<T>) => {
// return ...
};
};
const builder = classBuilder({
base: "text-white rounded-sm",
variants: {
color: {
red: "bg-red",
blue: "bg-blue",
},
size: {
sm: "text-sm",
lg: "text-lg",
},
},
});
// this should then take an object using the variant keys provided in the config
const classes = builder({ color: "blue", size: "sm" })
我期望返回函数的类型为:
const builder: (input: {
color: string;
size: string;
}) => string
然而,当鼠标悬停在VS代码中的builder函数上时,我得到了以下内容:
const builder: (input: TInput<{
base: string;
variants: {
size: {
sm: string;
md: string;
lg: string;
};
};
}>) => string
这似乎是输出泛型,而实际上没有提取变体,我不能为我的生活工作为什么。
如果任何人对此有任何指导,将不胜感激。
1条答案
按热度按时间4nkexdtk1#
需要说明的是,类型
TInput<{ base: string; variants: { color: { red: string; blue: string; }; size: { sm: string; lg: string; }; }; }>
与{color: string; size: string}
是相同的类型,就像(5×3)+2与17是相同的数字一样。您的问题不在于编译器没有正确地计算类型;只是它没有通过IntelliSense的快速信息以您期望的方式 * 显示 * 类型。TypeScript使用一堆启发式规则来决定如何显示类型。在保留类型别名以供显示和消除它们以供显示之间存在权衡,并且没有一种“正确”的方式来做到这一点,使每个人都满意,特别是有时候两个人会使用结构类似的代码,但想要不同的东西。正如microsoft/TypeScript#50941中提到的:
快速信息没有合同义务显示一个特定的形式或其他,也没有 * 总是 * 显示一个形式或其他(事实上,它有可能编写类型,* 不能 * 显示在某些形式)。
因此,如果您不喜欢显示
TInput<...>
,可以调整用法和定义以尝试获得不同的行为,但请记住,这种行为只是启发式的。到目前为止,最简单的方法是不要引入任何你不想在显示中看到的类型别名。用
TInput<T>
的定义替换它。如果你不想在显示中看到Record<K, V>
,也可以用它的定义替换它的使用。编译器认为你定义TInput<T>
是因为你想看到它,这并不是不合理的,在任何情况下,编译器肯定不会使用它不知道的东西。这就给了你这个:
它会给出你想要的输出:
如果你想保留
TInput<T>
的定义,那么你可以开始调整它的定义。How can I see the full expanded contract of a Typescript type?和microsoft/TypeScript#28508中介绍了其中的一些内容,但是一个简单的方法是给类型添加一个“no-op”并集或交集,这不会改变实际的输出类型,但是会鼓励编译器更完整地计算类型。例如:这里我们仍然没有使用
Record
(因为我们不想看到),但是现在我们与空对象类型{}
相交,任何与{}
相交的对象类型都将还原为对象类型,如果与unknown
类型相交或与never
类型并集,也会发生同样的情况。编译器通常更喜欢像这样急切地折叠交集/并集,而不是像TInput<{...}> & {}
那样保留它。因此,您也可以在此处获得所需的行为:Playground代码链接