Typescript部分但不是完整的对象本身



假设我有一个对象:

type Obj = { a: string, b: string, c: string }

对于Partial<T>, TS给出了对象属性的所有可能组合,包括对象的完整形式。在我的例子中,我想排除完整的对象(如果可能的话,也排除空的对象)。

所以我想要:

{} // ❌
{ a: string } // ✅
{ b: string } // ✅
{ c: string } // ✅
{ a: string, b: string } // ✅
{ a: string, c: string } // ✅
{ a: string, b: string, c: string } // ❌

我该怎么做呢?

要实现这种行为,您需要为Obj类型创建两个约束。第一个应该排除"满";键入和第二个- "empty"类型。


第一个约束意味着至少有一个属性应该是undefined类型的(或者根本不存在)。因此,我们需要一个类型的联合,其中至少有一个属性符合约束。

首先,我们必须映射初始类型以获得省略一个属性的所有可能的类型组合(注意?修饰符—它确保允许可变数量的属性),并提取与keyof T的联合:

{
[ P in keyof T ] : {
[ K in Exclude<keyof T, P> ] ?: T[P]
}
}[keyof T]

如果我们不这样做,我们仍然可以指定所有属性,所以我们需要显式地告诉编译器不允许第三个属性。?修饰符确保我们可以省略属性:

type NotAll<T> = {
[ P in keyof T ] : {
[ K in Exclude<keyof T, P> ] ?: T[P]
} & { [ M in P ] ?: never }
}[keyof T]

第二个约束意味着至少应该定义一个属性。逻辑几乎是相同的,但这次对于每个属性,我们都有一个类型的所有属性,除了一个可选的集合,与一个类型相交,该属性是必需的:

type AtLeastOne<T> = {
[ P in keyof T ] : {
[ K in Exclude<keyof T, P> ] ?: T[P]
} & { [ M in P ] : T[M] }
}[keyof T];

最后,我们需要将第二个约束与第一个约束结合起来。由于第一个约束给出了允许类型的联合,因此AtLeastOne应该应用于联合的每个成员:

type NotAll<T> = {
[ P in keyof T ] : AtLeastOne<{
[ K in Exclude<keyof T, P> ] : T[P] //note the modifier is moved to `AtLeastOne`
}> & { [ M in P ] ?: never }
}[keyof T];

就这样,让我们来测试我们的类型:

type test = NotAll<Obj>;
const empty : test = {}; //error
const a : test = { a: "test" }; //OK
const ab : test = { a: "test", b: "test" }; //OK
const bc : test = { b: "test", c: "test" }; //OK
const abc : test = { a: "test", b : "test", c: "test" }; //error

游乐场

相关内容

  • 没有找到相关文章

最新更新