我已经声明了一个像这样的对象:
const example = {
morning: (name) => `Good morning ${name}!`,
evening: (name) => `Good evening ${name}!`
}
在其原始形式中,该对象具有强类型键(我可以使用点表示法访问它们),但值可以是任何值。这就是为什么我决定引入类型签名:
const example: {
[K: string]: (name: string) => string;
} = {
morning: (name) => `Good morning ${name}!`,
evening: (name) => `Good evening ${name}!`
}
现在我的对象值是强类型的,但键不是,我可以尝试使用点表示法访问任何键,而无需 TypeScript 提示(例如,我可以编写 console.log(example.doesntexist)
,而且我也不会获得对象键的编辑器自动完成功能)。所以我尝试了这个:
const example: {
[K in keyof typeof example]: (name: string) => string;
} = {
morning: (name) => `Good morning ${name}!`,
evening: (name) => `Good evening ${name}!`
}
现在我收到循环约束错误(类型参数“K”具有循环约束
并且“example”在其自己的类型注释中直接或间接引用
)。我知道我可以做这样的事情
const example: {
[K in 'morning' | 'evening']: (name: string) => string;
} = {
morning: (name) => `Good morning ${name}!`,
evening: (name) => `Good evening ${name}!`
}
但这是多余的(我讨厌冗余/代码重复)。有办法做我想做的事吗?
最佳答案
如果没有一些额外的代码,就无法做到这一点。
在这种情况下,我通常会写一个 constrained通用identity function辅助函数。这与您尝试编写的循环注释非常相似:
const asExample = <K extends PropertyKey>(
x: { [P in K]: (name: string) => string }
) => x;
此函数仅接受类型为键 K
和值 (name: string) => string)
的输入,对于某些类似键的 K
编译器推断,并返回输入。由于该函数在 K
中是通用的,因此编译器将跟踪 K
,而不是扩展到诸如 string
之类的内容。
让我们测试一下:
const example = asExample({
morning: name => `Good morning ${name}!`,
evening: name => `Good evening ${name}!`
})
example.morning("Alice") // okay
example.day // error!
// ---> ~~~
// Property 'day' does not exist on type
// '{ morning: (name: string) => string; evening: (name: string) => string; }'
您可以看到 example
已知在 morning
和 evening
键处具有属性,但在 day
键。
如果您尝试在错误的输入上调用 asExample()
,您将得到您想要的错误:
const badExample = asExample({
afternoon: name => `Good afternoon ${name}!`.length, // error!
// --------------> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// number is not assignable to string
night: "Good night everyone!" // error!
// ~~~~~ <-- string is not assignable to (name: string) => string
})
关于javascript - 强类型对象中的 TypeScript 循环约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67761901/