如果您想只允许列表中的一个属性,您需要一个对象类型的union,其中每个对象类型都允许一个特定的属性而不允许所有其他属性。 TypeScript 并不完全允许您禁止特定属性,但您可以做一些接近的事情:将其设置为值类型为 never 的可选属性。在实践中,这将允许undefined 类型的属性,但undefined 属性和缺失属性之间并没有太大区别(并且在使用普通编译器选项see ms/TS#13195 的TypeScript 中没有很好地捕捉到差异)。
因此,对于上面的示例,您想要的类型如下所示:
type ICartManual = {
property1: string;
property2?: undefined;
someOtherProperty: string;
} | {
property1?: undefined;
property2: string;
someOtherProperty: string;
}
您可以验证它的行为是否符合您的要求:
const i1: ICartManual = {
property1: "prop1",
someOtherProperty: "other"
}
const i2: ICartManual = {
property2: "prop2",
someOtherProperty: "other"
}
const iBoth: ICartManual = { // error!
// ~~~~~ <-- property1 is incompatible with undefined
property1: "prop1",
property2: "prop2",
someOtherProperty: "other"
}
const iNeither: ICartManual = { // error!
// ~~~~~~~~ <-- property2 is missing
someOtherProperty: "other"
}
如果你有一个很大的接口,并且想要使用两种对象类型 T 和 U 并创建一个新的对象类型,它只需要 T 中的一个属性和 U 中的所有属性,你可以像这样定义它这个:
type OneKeyFrom<T, M = {}, K extends keyof T = keyof T> = K extends any ?
(M & Pick<Required<T>, K> & Partial<Record<Exclude<keyof T, K>, never>>) extends infer O ?
{ [P in keyof O]: O[P] } : never : never;
这使用了一堆 mapped 和 conditional 类型来构建你想要的联合。我可以解释它是如何工作的,但这需要很多话。我以前做过类似的事情;查看here 以获得对类似类型的更深入描述。
无论如何,我们现在可以这样定义ICart:
type ICart = OneKeyFrom<{ property1: string, property2: string }, { someOtherProperty: string }>;
并且您可以验证(例如通过 IntelliSense)它与手动编写的类型相同(除了写入属性的顺序,它不会更改类型):
/* type ICart = {
property1: string;
property2?: undefined;
someOtherProperty: string;
} | {
property2: string;
property1?: undefined;
someOtherProperty: string;
} */
Link to code