typescript - 根据其他属性更改接口属性的类型
问题描述
我正在处理网格(在 React 中),我有以下类型和接口(您可以在此处加载代码):
export type DataGridColumnType = 'currency' | 'date' | 'number' | 'text';
interface CurrencyColumnOptions {
symbol: string;
}
interface DateColumnOptions {
format: string;
}
interface NumberColumnOptions {
decimalPoint: string;
thousandSeparator: string;
}
type TextColumnOptions = {};
export interface DataGridColumn {
field: string;
label: string;
title: string;
columnOptions?: {}; // either CurrencyColumnOptions, DateColumnOptions, NumberColumnOptions or TextColumnOptions based on the "type" property
type?: DataGridColumnType;
}
export interface DataGrid {
columns: DataGridColumn[];
rows: Record<string, unknown>[];
}
const column: DataGridColumn = {
field: 'id',
label: 'ID',
title: 'ID',
type: 'date',
columnOptions: {
format: 'YYYY-mm-dd',
},
};
我想根据 的值更改属性columnOptions
的类型,以便当“类型”为“日期”时,它将使用 DateColumnOptions属性。DataGridColumn
type
columnOptions
我知道我可以在这里创建一个泛型,但是这些接口在我的网格系统内部有几层,拥有一个泛型会使使用网格变得更加复杂。
我环顾四周,但我不确定要搜索的正确术语。
这是可以做到的吗?还是我必须创建多个接口并使用联合类型?
谢谢
解决方案
当您想以某种方式约束一个对象类型的两个或多个属性时,您需要使该对象类型成为可接受配置的联合,或者使受约束的属性以某种方式依赖于泛型类型参数的泛型类型.
在您的示例中,该type
属性是一个字符串文字,您可以使用它来确定该columnOptions
属性的类型。这是判别联合的理想用例,type
作为判别式。
第一:aninterface
不能是联合类型;因此,如果您想DataGridColumn
成为受歧视的工会,则需要将其设为type
别名;这可能很好,但是aliases 和s之间有一些区别type
interface
。
现在您可以手动显式地写出DataGridColumn
联合,但这会很烦人且多余:
// Don't do this
type DataGridColumn = {
field: string;
label: string;
title: string;
type: "number";
columnOptions?: {
decimalPoint: string;
thousandSeparator: string;
};
} | {
field: string;
label: string;
title: string;
type: "currency";
columnOptions?: {
symbol: string;
}
} | { // sigh, I'm bored, forget it
相反,您可以进行一些重构并使用一些类型操作来让编译器DataGridColumn
根据一些非冗余类型以编程方式定义:
首先,我们可以为真正常见的字段创建一个接口:
interface BaseDataGridColumn {
field: string;
label: string;
title: string;
}
然后我们可以定义我们用作键和值之间type
和columnOptions
位置之间的映射:type
columnOptions
interface DataGridColumnOptions {
currency: {
symbol: string;
},
date: {
format: string;
}
,
number: {
decimalPoint: string;
thousandSeparator: string;
},
text: {}
}
请注意,我们不打算拥有 type 的值DataGridColumnOptions
;我们将使用它来定义DataGridColumn
:
type DataGridColumnType = keyof DataGridColumnOptions;
type DataGridColumn = { [K in DataGridColumnType]: BaseDataGridColumn & {
type: K,
columnOptions?: DataGridColumnOptions[K]
}}[DataGridColumnType]
首先,我们DataGridColumnType
通过获取 的键重新创建您的DataGridColumnOptions
,然后我们通过映射属性来生成DataGridColumn
联合。我们将每个键分配为属性,将每个值分配为属性。我们将这对属性与接口相交以获得扩展类型(相交的行为类似于扩展接口,但它以编程方式工作)。然后我们索引到这个映射类型,将映射类型转换为我们想要的可区分联合。DataGridColumnType
K
type
DataGridColumnOptions[K]
columnOptions
BaseDataGridColumn
DataGridColumnType
您可以验证这是否按需要工作:
const column: DataGridColumn = {
field: 'id',
label: 'ID',
title: 'ID',
type: 'date',
columnOptions: {
format: 'YYYY-mm-dd',
},
}; // okay
const badColumn: DataGridColumn = {
field: 'id',
label: 'ID',
title: 'ID',
type: 'currency',
columnOptions: {
format: 'YYYY-mm-dd', // error!
//~~~~~~~~~~~~~~~~~~
//Type '{ format: string; }' is not assignable
//to type '{ symbol: string; }'.
},
};
这是我可能会在这里采取的主要方法。如果这对你有用,那就太好了。您可能还需要进行一些其他更改和重构以简化或以不同方式显示内容,但除非按下,否则我不会沿着这条路线走得更远。
推荐阅读
- kubernetes - 在 GKE 中,“kubectl get nodes”或“kubectl get pods”不起作用
- javascript - ngx-extended-pdf-viewer/assets/pdf.js 不存在
- cypress - 启用重试功能时赛普拉斯执行不间断的首次尝试
- android - 构建没有随着反应原生 Android 的变化而更新
- docker - 如何在容器启动时运行命令,并在命令完成后保持容器运行?
- angular - Angular 12:架构验证失败,出现以下错误:数据路径“”不应具有其他属性(inlineStyleLanguage)
- node.js - NodeJS 谷歌云服务账户不记名令牌
- javascript - 在 JS 编译时 React DOM 更新。但是刷新浏览器会给出错误'无法读取未定义的属性(读取'1')'
- pdf - 下载 pdf - 屏幕阅读器的辅助功能
- android - Android KeyStore CTS ImportWrappedKeyTest 错误 - INVALID_ARGUMENT(代码 -38)