我们可以做到这一点:
interface IMyInterface{
firstname:string // this means firstname is mandatory
name:string // this also means name is mandatory
}
我怎么说,firstname
或name
中的任何一个是可选的(?
),具体取决于是否提供了另一个?
或者,如果那不可能,那么其他选择是什么?
编辑: 这不是Typescript Interface - Possible to make "one or the other" properties required?的重复。
我们不想为每个可选元素创建一个单独的接口,因为维护和命名以及重构将是一个痛苦的问题并且它不可重用。
答案 0 :(得分:3)
这是一种说“OneOf”这些键的通用方法,您可以在这里使用它:
type EachOfTmp<T> = {// to make OneOf less gross
[K in Keys<T>]: {
_: {[X in K]: T[K]};
}
};
// require only one of the keys
export type OneOf<T> = EachOfTmp<T>[Keys<T>]["_"] & Partial<T>;
const thing1: OneOf<{ a: number; b: number }> = { a: 2 } // valid
const thing2: OneOf<{ a: number; b: number }> = { b: 2 } // valid
const thing3: OneOf<{ a: number; b: number }> = {} // invalid
编辑:哎呀,我忘了我用这个方便的钥匙交易 -
export type Keys<T> = keyof T;
export function Keys<T>(o: T) {
if (!o) {
return [];
}
return Object.keys(o) as Keys<T>[];
}
答案 1 :(得分:3)
另一种方式:
interface IName {
name:string
}
interface IFirstName {
firstname:string
}
let x: IName | IFirstName;
x = {}; // Error
x = { name: "" }; // Ok
x = { firstname: "" }; // Ok
x = { name: "", firstname: "" }; // Ok
答案 2 :(得分:0)
如果IMyInterface
有其他需要保留的成员,我想提出@Catalyst响应的更通用版本。
type EachExpanded<T> = {
[key in keyof T]: { [subKey in key]: T[key]; }
};
type FixedSubset<T, U> = Pick<T, Exclude<keyof T, U>>;
type AtLeastSubset<T, U> = Pick<T, Extract<keyof T, U>>;
type AtLeaseOne<T, U> = FixedSubset<T, U> & EachExpanded<AtLeastSubset<T, U>>[keyof AtLeastSubset<T, U>];
const example1: AtLeaseOne<{ a: number; b: number; c: string; }, 'a' | 'b'> =
{ a: 3, b: 4, c: '4' } // valid
const example2: AtLeaseOne<{ a: number; b: number; c: string; }, 'a' | 'b'> =
{ a: 1, c: '1' } // valid
const example3: AtLeaseOne<{ a: number; b: number; c: string; }, 'a' | 'b'> =
{ b: 2, c: '2' } // valid
const example4: AtLeaseOne<{ a: number; b: number; c: string; }, 'a' | 'b'> =
{ c: '3' } // invalid
请注意,此响应使用刚刚发布的TypeScript版本2.8中引入的Exclude
和Extract
关键字。这些是条件类型的一部分。
在代码中,我们假设类型T
是原始类型或接口,U
是密钥集,其中至少必须存在一个。
它的工作方式是通过排除需要基于原始类型T
实现的属性来创建类型。这是使用Pick<T, Exclude<keyof T, U>>
定义的,其中包含U
以外的所有内容。
然后,创建另一个类型,其中只包含至少必须存在的元素Pick<T, Extract<keyof T, U>>
。
EachExpanded
存储同一个键下每个特殊集的类型。例如,如果上述示例中的密钥'a'
和'b'
有条件可选,则EachExpanded
会创建以下类型:
{
a: { a: number; };
b: { b: number; };
}
这将在带有交叉算子的最终类型中使用,因此至少有一个被强制存在。
基本上,对于上面的例子,我们最终会得到以下结论:
{ c: string; } & ({ a: number; } | { b: number; })
答案 3 :(得分:0)
您可以这样写。
interface ContactName{
firstname?: string;
name: string;
}
interface ContactFirstName{
firstname: string
name?: string
}
type Contact = ContactName | ContactFirstName;
当您使用“联系人”界面时,姓名或姓氏都变为必填项。