TypeScript 接口可以被识别为它们继承的接口吗?

jdl*_*lin 3 javascript inheritance interface typescript

我有一个 TypeScript 接口,可以通过许多其他接口进行扩展。例如,调用它Parent并说 2 个接口扩展Parent并称为ChildAChildB。事实上,还有更多的孩子继承了Parent

interface Parent {
  sharedProp: string;
}

interface ChildA extends Parent {
  uniqueChildAProp: string;
}

interface ChildB extends Parent {
  uniqueChildBProp: string;
}
Run Code Online (Sandbox Code Playgroud)

有没有办法让我拥有另一个Grandparent定义类似于如下的接口:

interface Grandparent {
  children: Parent[];
}
Run Code Online (Sandbox Code Playgroud)

数组中的元素children可以是ChildAor ChildB(或扩展的任何其他接口Parent)?

这将是一个有效的Grandparent

const concrete: Grandparent = {
  children: [
    {
      sharedProp: 'fooA',
      uniqueChildAProp: 'barA'
    },
    {
      sharedProp: 'foo',
      uniqueChildBProp: 'barB'
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

我想避免合并类型,ChildA, ChildB, etc.因为可能有很多Parent.

jca*_*alz 5

如果您想将Grandparent的属性限制为 的已知扩展的children数组,那么联合是唯一的方法。另一方面,如果您希望接受所有结构上兼容的扩展,甚至是尚未声明的扩展……那么您根本不需要更改定义。ParentParent

您现有的Grandparent定义已经允许属性的元素children是 a或类似或Parent的任何子类型。您可能遇到的问题是,当您像使用 一样使用对象文字时,编译器会执行过多的属性检查并抱怨它不期望的属性:ParentChildAChildBconcrete

const concrete: Grandparent = {
  children: [
    {
      sharedProp: 'fooA',
      uniqueChildAProp: 'barA' // error!
    },
    {
      sharedProp: 'foo',
      uniqueChildBProp: 'barB' // error!
    }
  ]
};
Run Code Online (Sandbox Code Playgroud)

但这并不意味着您不能通过ChildAor ChildB;它只是意味着您需要告诉编译器您打算使用ChildAorChildB以便属性不会出现意外:

const cA: ChildA = { sharedProp: 'fooA', uniqueChildAProp: 'barA' };
const cB: ChildB = { sharedProp: 'foo', uniqueChildBProp: 'barB' };
const concrete1: Grandparent = { children: [cA, cB] };
Run Code Online (Sandbox Code Playgroud)

或者,您可以通过许多其他方式进行重构来安抚编译器并避免过多的属性检查:

const children = [
  {
    sharedProp: 'fooA',
    uniqueChildAProp: 'barA'
  },
  {
    sharedProp: 'foo',
    uniqueChildBProp: 'barB'
  }
];
const concrete2: Grandparent = { children }; // okay
Run Code Online (Sandbox Code Playgroud)

如果您希望允许 的子类型Parent而不抱怨对象文字中的额外属性,您可能会决定使用索引签名,因为对象文字与适当的索引签名隐式兼容。像这样:

interface AnyParent extends Parent {
  [k: string]: unknown;
}

interface Grandparent2 {
  children: AnyParent[];
}

const concrete3: Grandparent2 = {
  children: [
    {
      sharedProp: 'fooA',
      uniqueChildAProp: 'barA'
    },
    {
      sharedProp: 'foo',
      uniqueChildBProp: 'barB'
    }
  ]
}; // okay
Run Code Online (Sandbox Code Playgroud)

这适用于您的示例代码。但要小心;根据microsoft/TypeScript#15300interface类型与索引签名ChildA隐式兼容,因此使用显式注释将不再起作用:

const childA: ChildA = { sharedProp: "", uniqueChildAProp: "" };
const oops: Grandparent2 = { children: [childA] }; // error!
Run Code Online (Sandbox Code Playgroud)

因此,只有当您只关心对象文字和类型别名而不关心接口时,才真正建议使用索引签名。因此,我可能会建议第一个选项并重构、注释或断言您有意添加额外的属性。


好的,希望有帮助;祝你好运!

Playground 代码链接