如何在接口内部声明强制一个对象扩展另一个对象?
场景:我收到一个JSON对象(称为document),其中的文档。孩子是一个对象数组,总有_id _ref _type,以及附加字段特定_type。
目前有4种不同的类型,但这将增长,理想情况下,我不希望未来的开发人员不得不担心编辑文档界面
export interface BaseRefs {
_id: string;
_ref: string;
_type: string;
}
export interface Span extends BaseRefs {
text: string;
}
export interface MainImage extends BaseRefs {
url: string;
caption: string;
}
export interface Document extends BaseRefs {
_createdAt: string;
_rev: string;
_updatedAt: string;
children: // Any object that extends BaseRefs
// children: MainImage | Span | Carousel | ........ | Video[] // This is not ideal
}
export const document: Document = {
_createdAt: '2019-12-12T04:14:18Z',
_id: 'c9b9-4cd0-a281-f6010f5889fd',
_ref: 'ej2gcz5m4',
_rev: 'nwyfsi--ej2gcz5m4',
_type: 'post',
_updatedAt: '2020-01-16T11:22:32Z',
children: [
{
_type: 'span',
text: 'The rain in Spain',
},
{
_type: 'mainImage',
url: 'https://example.com/kittens.png',
},
],
};
如何在接口内部声明强制一个对象扩展另一个对象?
你不能这么做字面上。TypeScript的类型系统是结构化的,而不是标称的。您可以要求它具有BaseRefs
定义的所有属性,但不能要求它实际上是extends
BaseRefs
。
据我所知,children
的类型应该是BaseRefs[]
:
export interface Document extends BaseRefs {
_createdAt: string;
_rev: string;
_updatedAt: string;
children: BaseRefs[];
}
要求children
中的元素都具有BaseRefs
中定义的所有属性。它们可以有更多的属性,所以Span
和MainImage
等都可以,但它们必须至少有BaseRefs
定义的属性。
这意味着你可以安全地在children
中的元素上使用_id
、_ref
和_type
,但是如果你试图使用其他东西(比如Span
的text
属性),TypeScript会抱怨BaseRefs
上没有text
属性。
for (const child of someDocument.children) {
console.log(child.text);
// ^−−−−−−−−−−−−−−−−−− error here
}
这是因为child
可能不是Span
,或者更一般地说,可能没有text
属性。
通过使用类型保护获得对该属性的访问权。一种类型保护是类型保护函数,它看起来像这样:
function isSpan(x: BaseRefs): x is Span {
return "text" in x;
}
:
for (const child of someDocument.children) {
if (isSpan(child)) {
console.log(child.text);
// ^−−−−−−−−−−−−−−−−−− works
}
}