【问题标题】:Typescript interface array must implement a base interfaceTypescript 接口数组必须实现一个基本接口
【发布时间】:2021-10-02 09:09:24
【问题描述】:

如何在接口中声明强制一个对象扩展另一个对象?

场景: 我收到一个 JSON 对象(称为文档),其中 document.children 是一个始终具有 _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 interface


    【解决方案1】:

    如何在接口中声明强制一个对象扩展另一个对象?

    你不能这样做字面意思。 TypeScript 的类型系统是 structural,而不是 nominal。您可以要求它具有BaseRefs 定义的所有属性,但实际上不是extends BaseRefs

    据我所知,children 的类型应该是BaseRefs[]

    export interface Document extends BaseRefs {
      _createdAt: string;
      _rev: string;
      _updatedAt: string;
      children: BaseRefs[];
    }
    

    这要求children 中的元素都具有BaseRefs 定义的所有属性。它们可以有更多属性,所以SpanMainImage等都可以,但它们至少必须具有为BaseRefs定义的属性。

    这意味着您可以安全地在children 中的元素上使用_id_ref_type,但是如果您尝试使用其他东西(例如Spantext 属性),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
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-08
      相关资源
      最近更新 更多