typescript - 在下游接口上保留基于字段内容的联合类型区分
问题描述
这是场景。
我有一个表示泛型元数据的接口Field
(假设我们正在谈论MatFormField
来自 Angular Material 的实例),它包含一个名称和一个类型,它是一个常见输入类型的列表。
export type FieldType = 'string' | 'number' | 'date' | 'datetime' | 'select';
export interface FieldMetadata {
readonly type: FieldType;
name: string;
}
有些Field
s 只使用这些元数据就可以了,而另一些则需要额外的信息,因此需要接口扩展。然后我定义了一个联合类型,所以我可以根据“类型”字段获得正确的类型提示。
// Specific interface for select field
export interface SelectFieldInterface extends FieldMetadata {
readonly type: 'select';
options: string[];
}
// All other fields which doesn't need additional info
export interface GenericFieldInterface extends FieldMetadata {
readonly type: Exclude<FieldType, 'select'>;
}
export type Field = GenericFieldInterface | SelectFieldInterface;
// Understands it's a GenericFieldInterface
const genericField1: Field = {
type: 'number',
name: ''
};
const genericField2: Field = {
type: 'number',
name: '',
option: [] // <---- Type Error, unexpected 'options'
};
// Understands it's a SelectFieldInterface
const selectField1: Field = {
type: 'select',
name: '',
options: []
};
const selectField2: Field = {
type: 'select',
name: '',
// <----- Type Error, expected 'options'
};
在这里一切都按预期工作(但如果您知道更好的管理方法,请分享)。
当我想建立在这个抽象之上时,问题就出现了。
假设应该在这种联合类型之上构建一个新模型以保持区分。我希望这样的事情能够得到这样的结果。
export interface Model extends Field {
additionalProperty: string;
}
但是在 TypeScript 中这是不可行的,你不能扩展联合类型。我通过定义一个接受所有Field
s 类型的所有属性的中间“超类型”部分解决了这个问题。
我不得不在特定实现上省略类型属性,否则会出现与接受的类型值冲突的问题。
export type ExtendedField = FieldMetadata &
Partial<Omit<GenericFieldInterface & SelectFieldInterface, 'type'>>;
但是这样我失去了两件事。
首先是各种实施的区别。
const genericField: Model = {
type: 'number',
name: '',
additionalProperty: '',
option: [] // <---- Doesn't fire Type Error for 'option' property, it should
};
const selectField: Model = {
type: 'select',
name: '',
additionalProperty: '',
// <---- Doesn't fire Type Error for missing 'option' property, it should
};
第二个是Field
和Model
签名之间的兼容性:我不能Model
在签名要求的地方使用Field
. 当然我知道它们是兼容的,但是编译器不兼容,我不得不一直强制Model
转换Field
。
function someFn(field: Field) {}
const selectField: Model = {
type: 'select',
name: '',
additionalProperty: '',
option: []
};
someFn(selectField) <------ Type Error
someFn(selectField as Field) <------ Works with casting
关于如何更好地代表这些约束的任何想法?我试图研究条件类型,但我不明白它们是否对我有用......
解决方案
解决您的问题的最简单方法是使用带有交集的类型别名,添加您需要的额外属性。只要这些类型仅用于类型检查对象文字并且不必由类实现,它应该都能正常工作:
type Model = Field & {
additionalProperty: string;
}
const genericField: Model = {
type: 'number',
name: '',
additionalProperty: '',
option: [] // err
};
const selectField: Model = { //err
type: 'select',
name: '',
additionalProperty: '',
};
推荐阅读
- php - Laravel 5.8 StreamDownload 文件附件重定向而不是下载
- github - 为标签重新运行 Github 操作更新
- solr - 希望改进多值字段上的 Solr 过滤器查询,这需要超过 2 秒
- javascript - 用另一个 Jquery 函数触发一个 Jquery 函数
- c# - dotnet 测试例程失败
- asp.net - 在 .net 核心中,如何在 Web 根目录下下载具有特殊扩展名的静态文件?
- azure-devops - 连接到 Azure DevOps 服务时,如何在 Visual Studio 而不是 Web 浏览器中打开 Bug/PBI?
- python-3.x - 请解释以下代码的工作原理和下面提到的一些问题
- python - QTreeView QAbstractItemModel 父级在删除项目后崩溃,有时崩溃
- parsing - 在确定标记之前让词法分析器考虑解析器?