我需要使选项卡组件能够显示选项卡在图标模式或纯文本.与其传入variant,我想也许更好的做法是让打字脚本判断其中一个图标是否具有iconName属性,如果是的话-强制所有其他项也包括它(通过抛出编译器错误).如果所有对象都有iconName,或者并非所有对象都有iconName,则它是有效的.

这里是一个例子,也是我的try ,以使其更清楚:

type ObjectWithIcon<T> = T extends { iconName: string } ? T & { iconName: string } : T;

interface MyObject {
  iconName?: string;
  label: string;
}

// This will throw error on second object because of missing iconName
const arrayOfObjects: ObjectWithIcon<MyObject>[] = [
  { iconName: "box", label: "storage" },
  { label: "memory" }, // Error: Property 'iconName' is missing, currently NOT throwing an error
];


// This will not give any error because both has iconName
const arrayOfObjects2: ObjectWithIcon<MyObject>[] = [
  { iconName: "box", label: "storage" },
  { iconName: "circle", label: "memory" },
];

// This will not give any error because no object using iconName
const arrayOfObjects3: ObjectWithIcon<MyObject>[] = [
  { label: "storage" },
  { label: "memory" },
];

推荐答案

无论你如何定义ObjectWithIcon<T>,你都不想使用类型ObjectWithIcon<MyObject>[],因为它只是一个普通数组类型.这样一个数组类型不可能关心它的所有元素是否都有某个特定的属性,它唯一关心的是每个元素是否符合ObjectWithIcon<MyObject>,独立于数组中的其他元素.

相反,我认为你应该使用union个数组类型,其中你说"这是either一个数组MyObjectwithiconNameor一个数组MyObjectwithouticonName.

也就是说,您需要一个等价于

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的东西,以及类型neverK属性是可选的东西(通过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 ofWith 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

Typescript相关问答推荐

React组件数组及其props 的类型定义

react 路由6操作未订阅从react 挂钩表单提交+也不使用RTK查询Mutations

与字符串文字模板的结果类型匹配的打字脚本

有条件地删除区分的联合类型中的属性的可选属性

如何在TypeScrip中正确键入元素和事件目标?

是否存在类型联合的替代方案,其中包括仅在某些替代方案中存在的可选属性?

Angular 15使用react 式表单在数组内部创建动态表单数组

`fetcher.load`是否等同于Get Submit?

类型脚本可以推断哪个类型作为参数传递到联合类型中吗?

如何通过TypeScrip中的函数防止映射类型中未能通过复杂推理的any值

类型推断对React.ForwardRef()的引用参数无效

如何避免从引用<;字符串&>到引用<;字符串|未定义&>;的不安全赋值?

如何在不违反挂钩规则的情况下动态更改显示内容(带有状态)?

打字脚本错误:`不扩展[Type]`,而不是直接使用`[Type]`

有没有办法在Reaction组件中动态导入打字脚本`type`?

有没有一种更好的方法来存储内存中的数据,而不是在类型脚本中使用数组和基于索引的函数?

我可以使用JsDocs来澄清Typescript实用程序类型吗?

React-跨组件共享功能的最佳实践

如何在由函数参数推断的记录类型中具有多个对象字段类型

广义泛型类型不加载抽象类型上下文