强类型对象中的 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) =&gt; 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 已知在 morningevening 键处具有属性,但在 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 循环约束的主要内容,如果未能解决你的问题,请参考以下文章

typescript个人总结

Typescript 强类型键值对声明

Typescript 索引属性约束检查适用于原始类型但不适用于对象文字?

TypeScript 中的强类型剩余参数

Typescript杂谈

Typescript中的接口