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

Jih*_*ada 4 typescript mapped-types

假设我有一个对象:

\n
type Obj = { a: string, b: string, c: string }\n
Run Code Online (Sandbox Code Playgroud)\n

通过Partial<T>,TS 为您提供对象属性的所有可能组合,包括对象的完整形式。就我而言,我想排除完整的对象(如果可能的话,也排除空的对象)。

\n

所以我想要:

\n
{} // \xe2\x9d\x8c\n{ a: string } // \xe2\x9c\x85\n{ b: string } // \xe2\x9c\x85\n{ c: string } // \xe2\x9c\x85\n{ a: string, b: string } // \xe2\x9c\x85\n{ a: string, c: string } // \xe2\x9c\x85\n{ a: string, b: string, c: string } // \xe2\x9d\x8c\n
Run Code Online (Sandbox Code Playgroud)\n

我怎样才能做到这一点?

\n

Ole*_*ter 7

要实现此行为,您需要为该Obj类型创建两个约束。第一个应该排除“完整”类型,第二个应该排除“空”类型。


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

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

{
  [ P in keyof T ] : {
    [ K in Exclude<keyof T, P> ] ?: T[P]
  }
}[keyof T]
Run Code Online (Sandbox Code Playgroud)

如果我们保留它,我们仍然可以指定所有属性,因此我们需要明确告诉编译器不允许第三个属性。?修饰符确保我们可以省略该属性:

type NotAll<T> = {
  [ P in keyof T ] : {
    [ K in Exclude<keyof T, P> ] ?: T[P]
  } & { [ M in P ] ?: never }
}[keyof T]
Run Code Online (Sandbox Code Playgroud)

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

type AtLeastOne<T> = {
  [ P in keyof T ] : {
    [ K in Exclude<keyof T, P> ] ?: T[P]
  } & { [ M in P ] : T[M] }
}[keyof T];
Run Code Online (Sandbox Code Playgroud)

最后,我们需要将第二个约束与第一个约束结合起来。由于第一个约束为我们提供了允许类型的联合,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];
Run Code Online (Sandbox Code Playgroud)

就是这样,让我们​​测试一下我们的类型:

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
Run Code Online (Sandbox Code Playgroud)

操场