如何在 TypeScript 中输入具有已知和未知键的对象

2023-12-07

我正在寻找一种方法为以下对象创建 TypeScript 类型,该对象具有两个已知键和一个具有已知类型的未知键:

interface ComboObject {
  known: boolean
  field: number
  [U: string]: string
}

const comboObject: ComboObject = {
  known: true
  field: 123
  unknownName: 'value'
}

该代码不起作用,因为 TypeScript 要求所有属性都与给定索引签名的类型匹配。但是,我不想使用索引签名,我想输入一个我知道其类型但不知道其名称的字段。

到目前为止,我唯一的解决方案是使用索引签名并设置所有可能类型的联合类型:

interface ComboObject {
  [U: string]: boolean | number | string
}

但这有很多缺点,包括允许已知字段上的错误类型以及允许任意数量的未知键。

有更好的方法吗? TypeScript 2.8 条件类型有帮助吗?


你自找的。

让我们进行一些类型操作来检测给定类型是否是联合。它的工作方式是使用分配性的条件类型的属性将联合扩展到成分,然后注意每个成分都比联合更窄。如果这不是真的,那是因为工会只有一个成员(所以它不是工会):

type IsAUnion<T, Y = true, N = false, U = T> = U extends any
  ? ([T] extends [U] ? N : Y)
  : never;

然后用它来检测给定的stringtype 是单个字符串文字(所以:不是string, not never,而不是联合):

type IsASingleStringLiteral<
  T extends string,
  Y = true,
  N = false
> = string extends T ? N : [T] extends [never] ? N : IsAUnion<T, N, Y>;

现在我们可以开始讨论您的具体问题。定义BaseObject作为的一部分ComboObject您可以直接定义:

type BaseObject = { known: boolean, field: number };

并准备错误消息,让我们定义一个ProperComboObject这样当你搞砸时,错误会给出一些关于你应该做什么的提示:

interface ProperComboObject extends BaseObject {
  '!!!ExactlyOneOtherStringPropertyNoMoreNoLess!!!': string
}

主菜来了。VerifyComboObject<C>需要一个类型C如果符合您的要求,则原封不动地退回ComboObject类型;否则它返回ProperComboObject(它也不符合)错误。

type VerifyComboObject<
  C,
  X extends string = Extract<Exclude<keyof C, keyof BaseObject>, string>
> = C extends BaseObject & Record<X, string>
  ? IsASingleStringLiteral<X, C, ProperComboObject>
  : ProperComboObject;

它的工作原理是解剖C into BaseObject和其余的钥匙X. If C不匹配BaseObject & Record<X, string>,那么你就失败了,因为这意味着它要么不是BaseObject,或者它是一个带有额外的非string特性。然后,它确保有正好一个剩余密钥,通过检查X with IsASingleStringLiteral<X>.

现在我们创建一个辅助函数,它要求输入参数匹配VerifyComboObject<C>,并返回输入不变。如果您只想要正确类型的对象,它可以让您及早发现错误。或者您可以使用签名来帮助您自己的函数需要正确的类型:

const asComboObject = <C>(x: C & VerifyComboObject<C>): C => x;

让我们测试一下:

const okayComboObject = asComboObject({
  known: true,
  field: 123,
  unknownName: 'value'
}); // okay

const wrongExtraKey = asComboObject({
  known: true,
  field: 123,
  unknownName: 3
}); // error, '!!!ExactlyOneOtherStringPropertyNoMoreNoLess!!!' is missing

const missingExtraKey = asComboObject({
  known: true,
  field: 123
}); // error, '!!!ExactlyOneOtherStringPropertyNoMoreNoLess!!!' is missing

const tooManyExtraKeys = asComboObject({
  known: true,
  field: 123,
  unknownName: 'value',
  anAdditionalName: 'value'
}); // error, '!!!ExactlyOneOtherStringPropertyNoMoreNoLess!!!' is missing

第一个根据需要进行编译。最后三个失败的原因与额外属性的数量和类型有关。错误消息有点神秘,但这是我能做的最好的事情。

您可以在中查看正在运行的代码操场.


再说一次,我不认为我建议在生产代码中这样做。我喜欢玩类型系统,但这个感觉特别复杂而脆弱,并且我不想对任何事情负责不可预见的后果.

希望对您有帮助。祝你好运!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 TypeScript 中输入具有已知和未知键的对象 的相关文章

随机推荐