TypeScript接口能否要求多个字符串键或值相同?
假设我想创建一个TypeScript接口能否要求多个字符串键或值相同?,typescript,Typescript,假设我想创建一个界面,看起来像这样: 接口调色板{ defaultColorName:字符串//应以以下颜色存在 颜色:{ [颜色名称:字符串]:字符串//将颜色名称与颜色关联(十六进制字符串) } } 换句话说,此调色板概念允许指定颜色列表(每个颜色都可以通过任意颜色名称访问),但也可以提供默认颜色 我想知道在TypeScript中是否可以强制键/值对象defaultColorName中存在defaultColorName的值,因为指定colors中不存在的默认颜色是没有意义的 我知道如果颜
界面
,看起来像这样:
接口调色板{
defaultColorName:字符串//应以以下颜色存在
颜色:{
[颜色名称:字符串]:字符串//将颜色名称与颜色关联(十六进制字符串)
}
}
换句话说,此调色板概念允许指定颜色列表(每个颜色都可以通过任意颜色名称访问),但也可以提供默认颜色
我想知道在TypeScript中是否可以强制键/值对象defaultColorName
中存在defaultColorName
的值,因为指定colors
中不存在的默认颜色是没有意义的
我知道如果颜色名称是一个字符串联合,例如type ColorType='bright'|'dark'|'minute'
,我可以在接口中使用泛型,例如接口colorplate
。在本例中,颜色名称是任意的string
s,可以是任何颜色
我使用调色板的概念来演示我想要做的事情,我的实际用例并不特定于颜色。这里的关键因素是:
在本例中,颜色名称是任意字符串,可以是任何内容
由于这一规定,您所要求的是不可能的,因为您要求TypeScript编译器(令人惊讶的是,它只存在于编译时)根据运行时发生的事情做出决定(将任意字符串键/值对插入颜色
对象).Typescript typesafety仅在编译时使用。因此,编译器无法验证在编译时未知的任何内容
此外,如果没有显式的运行时检查,您甚至不能保证在编译时完全有效的所有假设在运行时仍然有效。想象一下,您正在从一个文件或API读取JSON数据,并期望该数据遵循某个接口。但是如果没有显式的运行时检查,可能会出现各种运行时错误或意外/未定义的行为。例如,属性可能丢失或类型错误。您当然可以在TypeScript中描述您正在讨论的约束,但不能作为特定类型。相反,您可以将泛型(如您所提到的)与助手函数一起使用,以便可以推断泛型类型参数,而不是手动指定
虽然TypeScript的类型系统确实在运行时不存在,但类型系统的思想是描述在运行时确实存在的值集。指出你事先不知道颜色名称是什么,这有点像转移视线。有一种实用程序类型,它的巨大实用性并没有因为我现在无法告诉您哪些特定键将位于K
中而降低
强烈键入colorplate
来表示约束可能有用,也可能不有用,这取决于是否值得在TypeScript代码周围拖动泛型类型参数。但这不同于说它没有用,因为类型系统被擦除了
例如:
interface ColorPalette<K extends string> {
colors: {
[P in K]: string // associate a color name to a color (hex string)
};
defaultColorName: NoInfer<K> // should exist in colors above
}
type NoInfer<T> = [T][T extends any ? 0 : never]; // see microsoft/TypeScript#14829
const asColorPalette = <K extends string>(
colorPalette: ColorPalette<K>) => colorPalette;
function useColorPalette<K extends string>(colorPalette: ColorPalette<K>) {
for (let k in colorPalette.colors) {
console.log(k + "->" + colorPalette.colors[k].toUpperCase());
}
console.log(
"The hex string corresponding to the default color is " +
colorPalette.colors[colorPalette.defaultColorName].toUpperCase()
);
colorPalette.colors.aquamarine; // error!
// Property 'aquamarine' does not exist on type '{ [P in K]: string; }'
}
在这里,编译器接受okaycolorplate
和differentcolorplate
,但拒绝badcolorplate
。因此,如果编写任何指定颜色名称的TypeScript代码,编译器将帮助您
即使您从未在TypeScript代码中实际看到具体的颜色名称,ColorPalette
类型仍然可以使用。大概您想编写一些TypeScript代码来操纵一些未知的K
的colorplate
值,对吗?例如:
interface ColorPalette<K extends string> {
colors: {
[P in K]: string // associate a color name to a color (hex string)
};
defaultColorName: NoInfer<K> // should exist in colors above
}
type NoInfer<T> = [T][T extends any ? 0 : never]; // see microsoft/TypeScript#14829
const asColorPalette = <K extends string>(
colorPalette: ColorPalette<K>) => colorPalette;
function useColorPalette<K extends string>(colorPalette: ColorPalette<K>) {
for (let k in colorPalette.colors) {
console.log(k + "->" + colorPalette.colors[k].toUpperCase());
}
console.log(
"The hex string corresponding to the default color is " +
colorPalette.colors[colorPalette.defaultColorName].toUpperCase()
);
colorPalette.colors.aquamarine; // error!
// Property 'aquamarine' does not exist on type '{ [P in K]: string; }'
}
函数useColorPalette(colorPalette:colorPalette){
for(让k在colorplate.colors中){
console.log(k+“->”+colorplate.colors[k].toUpperCase());
}
console.log(
“与默认颜色对应的十六进制字符串为”+
colorPalette.colors[colorPalette.defaultColorName].toUpperCase()
);
colorPalette.colors.aquamarine;//错误!
//属性“aquamarine”在类型“{[P in K]:string;}”上不存在
}
由于泛型类型,编译器对colorplate
有一点了解:它知道for..in
循环生成一个k
,可用于索引到colors
属性中;它知道defaultColorName
属性可以用作colors
属性的键;它知道一些随机字符串,比如“aquamarine”
,不一定可以用作colors
属性的键。如果您只是使用string
而不是K extends string
,编译器将允许使用aquamarine
索引
再说一次,这对你来说可能不值得。泛型类型比特定类型更难处理。但这并不像“别麻烦了,因为TypeScript在你运行任何东西之前就消失了”
如果在编译时不知道它们,不,您不能怀疑是这种情况,谢谢您的解释和确认!