你需要的到了。
让我们进行一些类型操作,以便检测给定类型是否为联合类型。其工作方式是使用条件类型的分配属性来将联合类型扩展到组成部分,然后注意到每个组成部分都比联合类型更窄。如果不是这样,那么就是因为联合类型只有一个组成部分(因此它不是联合类型):
type IsAUnion<T, Y = true, N = false, U = T> = U extends any
? ([T] extends [U] ? N : Y)
: never;
然后使用它来检测给定的{{string}}类型是否为单个字符串文字(因此不是{{string}},不是{{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
解析为
BaseObject
和其余的键
X
。如果
C
不符合
BaseObject & Record<X, string>
,那么就失败了,因为这意味着它要么不是一个
BaseObject
,要么是一个具有额外非
string
属性的
BaseObject
。然后,它通过使用
IsASingleStringLiteral<X>
检查
X
来确保仅剩下
一个键。
现在我们创建一个辅助函数,该函数要求输入参数与VerifyComboObject<C>
匹配,并返回未更改的输入。如果只想要正确类型的对象,则可以使用该函数及时捕捉错误。或者您可以使用签名来帮助使您自己的函数要求正确的类型:
const asComboObject = <C>(x: C & VerifyComboObject<C>): C => x
让我们来测试一下:
const okayComboObject = asComboObject({
known: true,
field: 123,
unknownName: 'value'
});
const wrongExtraKey = asComboObject({
known: true,
field: 123,
unknownName: 3
});
const missingExtraKey = asComboObject({
known: true,
field: 123
});
const tooManyExtraKeys = asComboObject({
known: true,
field: 123,
unknownName: 'value',
anAdditionalName: 'value'
});
第一个编译通过,符合要求。后面三个因为额外属性的数量和类型不同而失败。错误信息有点晦涩,但这是我能做到的最好的。
你可以在
Playground中看到代码的实际运行效果。
再次强调,我不认为我建议在生产代码中使用这种方法。我喜欢玩弄类型系统,但这个方法感觉特别复杂且脆弱, 我不想对任何意外后果负责。
希望这可以帮到你。祝好运!
props: { children: string[] | VDOMElement[] } & { [key: string]: string };
然而,如果不使用&
运算符将它们组合在一起,则无法正常工作:props: { children: string[] | VDOMElement[], [key: string]: string };
- CodeFinity& {[key: string]: string}
的方法,通常也无法将已知属性与字符串联合起来,因为已知属性不能与字符串进行联合:https://www.typescriptlang.org/play?#code/CYUwxgNghgTiAEkoGdnwGoBEDyBZAohCALYgB2ALvAN4C+AUPRQJ4AOC+AHlMa0fAF4aiABYBLCMDhkAXPGQUYYsgHMA2gF14AHww4CRUpU3xa8AGTC1AaxDM5Cpao0PFylacagkceADMAVzIwCjEAezJ4EG5eIgAKADcoCDkuHj4QAEo5BLCxYEZo9PjqMHFJaTk1AHIREAgIMOqNWkz4AHp2qJgYMJh4ACNwKADkBAADMokpcnHEKDIyMKoh+Ch5N1U1smBttZ6oZiA - Jacob Gillespietsconfig
来“作弊”。它非常严格。 - CodeFinity