无论你如何定义ObjectWithIcon<T>
,你都不想使用类型ObjectWithIcon<MyObject>[]
,因为它只是一个普通数组类型.这样一个数组类型不可能关心它的所有元素是否都有某个特定的属性,它唯一关心的是每个元素是否符合ObjectWithIcon<MyObject>
,独立于数组中的其他元素.
相反,我认为你应该使用union个数组类型,其中你说"这是either一个数组MyObject
,with和iconName
,or一个数组MyObject
的without和iconName
.
也就是说,您需要一个等价于
type ObjectsAllWithOrAllWithoutIcon = {
iconName: string; // <-- a required property
label: string;
}[] | {
iconName?: never; // <-- a prohibited property
label: string;
}[]
TypeScrip实际上不会直接编码"没有给定的属性",但您可以写成"Has optional property of the impossible never
type",这非常接近.
总之,为了达到这个目的,让我们编写一些实用程序类型来表示"with","without"和"either—or"数组:
type With<T, K extends keyof T> = T & Required<Pick<T, K>>;
type Without<T, K extends keyof T> = T & Partial<Record<K, never>>;
type ArrayAllWithOrAllWithout<T, K extends keyof T> =
With<T, K>[] | Without<T, K>[]
With<T, K>
类型表示你得到了类型T
的东西,并且(通过intersection),需要K
属性的东西(通过the Pick
utility type获取带有键K
的零件,通过the Required
utility type表示该属性是必需的,而不是可选的).
Without<T, K>
类型表示你得到了类型T
的东西,以及类型never
的K
属性是可选的东西(通过the Partial
utility type表示这个属性是可选的,通过the Record
utility type表示我们讨论的是键K
和值never
的东西).
And finally,
ArrayAll With或All Without T,K says you've got something which is either an array of
With T,K , or an array of
Without T,K `.<><><>
您可以验证
interface MyObject {
iconName?: string;
label: string;
}
type ObjectsAllWithOrAllWithoutIcon =
ArrayAllWithOrAllWithout<MyObject, "iconName">;
相当于上面手工写出的ObjectsAllWithOrAllWithoutIcon
.因此,您的示例可以按预期运行:
const arrayOfObjects: ObjectsAllWithOrAllWithoutIcon = [ // error!
// ~~~~~~~~~~~~~~
// Property 'iconName' is missing in type '{ label: string; }'
// but required in type 'Required<Pick<MyObject, "iconName">>'.
{ iconName: "box", label: "storage" },
{ label: "memory" },
];
const arrayOfObjects2: ObjectsAllWithOrAllWithoutIcon = [
{ iconName: "box", label: "storage" },
{ iconName: "circle", label: "memory" },
]; // okay
const arrayOfObjects3: ObjectsAllWithOrAllWithoutIcon = [
{ label: "storage" },
{ label: "memory" },
]; // okay
Playground link to code个