0

我有以下接口定义。

interface IComponents {
  root: IComponent,
  [key: string]: IComponent,
}

interface IComponent {
  type: string,
  children?: Array<keyof IComponents>;
}

我希望“子”属性只接受已定义组件的键。在“root.children”属性的情况下,它应该只接受 root、button1 和 button2:

const list: IComponents = {
  root: {
    type: 'panel',
    children: ['button1', 'button2', 'button3']
  },
  button1: {
    type: 'button'
  },
  button2: {
    type: 'button'
  },
}

但它也接受任意字符串,如示例“ button3 ”。

4

2 回答 2

0

但它也接受任意字符串,如示例“button3”。

原因:

你有

interface IComponents {
  root: IComponent,
  [key: string]: IComponent,
}

所以keyof IComponents解决'root' | string或有效string。您几乎总是希望在同一个组中string有明确定义的名称和索引器。

解决方案

我会重新考虑非循环设计。以下:

const list: IComponents = {
  root: {
    type: 'panel',
    children: ['button1', 'button2', 'button3']
  },
  button1: {
    type: 'button'
  },
  button2: {
    type: 'button'
  },
}

的类型list 取决于分配的对象。理想情况下,您会想出某种方式来强制执行可以分配的内容。

于 2018-10-16T23:14:31.550 回答
0

没有一种IComponents类型可以定义,包括所有(且仅)在内部一致的组件列表,因为children列表仅引用已定义的组件;这将需要一种存在类型。但是,您可以定义一个泛型类型,该类型IComponents<K>表示具有特定键列表的有效组件列表K,这将允许您在类型参数中定义泛型函数K并接受 an IComponents<K>,因此可以在任何有效组件列表上调用。例如:

type IComponents<K extends string> = {
  [P in K]: IComponent<K>;
} & {
  // Needed for contextual typing to work.
  // https://github.com/Microsoft/TypeScript/pull/27586 might remove the need for this.
  [n: string]: IComponent<K>
};

interface IComponent<K extends string> {
  type: string,
  children?: Array<K>;
}

function processComponents<K extends string>(arg: IComponents<K>) {
  // ...
}

// OK
processComponents({
  root: {
    type: 'panel',
    children: ['button1', 'button2']
  },
  button1: {
    type: 'button'
  },
  button2: {
    type: 'button'
  },
});

// Error (unfortunately it doesn't pinpoint the mistake)
processComponents({
  root: {
    type: 'panel',
    children: ['button1', 'button2', 'button3']
  },
  button1: {
    type: 'button'
  },
  button2: {
    type: 'button'
  },
});
于 2018-10-17T02:19:08.907 回答