这是this question 的副本。在投票结束之前,我会在那里调整我的答案。请注意,这是非常脆弱的,您会感觉到自己在与编译器作斗争。我的建议是将您的数据结构更改为更明显,例如interface SomeType {title:string; unknownPropName: string; unknownPropVal: string[]},并在您需要的任何地方转换为您的版本。你会更快乐。
这是一个丑陋的疯狂通用解决方法,它试图检测你有多少额外的键:
// detect if T is a union
type IsAUnion<T, Y = true, N = false, U = T> = U extends any
? ([T] extends [U] ? N : Y)
: never;
// detect if T is a single string literal
type IsASingleStringLiteral<
T extends string,
Y = true,
N = false
> = string extends T ? N : [T] extends [never] ? N : IsAUnion<T, N, Y>;
type BaseObject = { title: string };
// if C conforms to desired ComboObject, return C.
type VerifyComboObject<
C,
X extends string = Extract<Exclude<keyof C, keyof BaseObject>, string>
> = BaseObject & Record<
IsASingleStringLiteral<X, X, "!!!ExactlyOneUnknownPropertyRequired!!!">,
string[]
>
// only accept parameters of type C that extend VerifyComboObject<C>
const asComboObject = <C>(x: C & VerifyComboObject<C>): C => x;
// testing
const okayComboObject = asComboObject({
title: "Foo",
unknownName: ["A", "B"]
}); // okay
const wrongExtraKey = asComboObject({
title: "Foo",
unknownName: 3
}); // error, number not assignable to string[]
const missingExtraKey = asComboObject({
title: "Foo",
}); // error, '!!!ExactlyOneUnknownPropertyRequired!!!' is missing
const tooManyExtraKeys = asComboObject({
title: "Foo",
unknownName: ["A", "B"],
anAdditionalName: ["A", "B"]
}); // error, '!!!ExactlyOneUnknownPropertyRequired!!!' is missing
Playground link