强类型对象中的 TypeScript 循环约束
Posted
技术标签:
【中文标题】强类型对象中的 TypeScript 循环约束【英文标题】:TypeScript circular constraint in a strongly typed object 【发布时间】:2021-08-18 01:28:23 【问题描述】:我已经这样声明了一个对象:
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!`
现在我遇到了循环约束错误(Type parameter 'K' has a circular constraint
和 'example' is referenced directly or indirectly in its own type annotation
)。我知道我可以做这样的事情
const example:
[K in 'morning' | 'evening']: (name: string) => string;
=
morning: (name) => `Good morning $name!`,
evening: (name) => `Good evening $name!`
但那是多余的(我讨厌冗余/代码重复)。有没有办法做我想做的事?
【问题讨论】:
"但值可以是任何值" - 不,它们是字符串返回函数。这有什么问题? @Bergi 我想要求所有值都是字符串返回函数,以避免出现错误 为什么要为example
对象声明类型?ts 会为您推断!
@Nur 正如我已经说过的,我正在设置一个类型以要求所有对象值都必须是字符串返回函数,因此我可以期望对象中的每个项目都有相同的行为
另外你想要自动完成对吗?据我所知,您还需要指定 keys
,例如 let example: Record<'morning' | 'evening', () => string>;
【参考方案1】:
如果没有至少一些额外的代码,就没有办法做到这一点。
在这种情况下,我通常会编写一个 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
)
Playground link to code
【讨论】:
以上是关于强类型对象中的 TypeScript 循环约束的主要内容,如果未能解决你的问题,请参考以下文章