我有以下接口定义。
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 ”。
答案 0 :(得分:0)
但是它也接受任意字符串,例如示例“ button3”。
您有
interface IComponents {
root: IComponent,
[key: string]: IComponent,
}
因此keyof IComponents
解析为'root' | string
或有效地string
。您几乎总是从不希望拥有定义明确的名称和string
索引器in the same group。
我会重新考虑非循环设计。以下:
const list: IComponents = {
root: {
type: 'panel',
children: ['button1', 'button2', 'button3']
},
button1: {
type: 'button'
},
button2: {
type: 'button'
},
}
list
的类型取决于分配的对象。理想情况下,您将找出某种类型的方法来强制执行 可以分配的内容。
答案 1 :(得分:0)
您无法定义单个IComponents
类型,该类型包括在内部children
仅指已定义的组件的内部一致性的所有(且仅)组件列表。这将需要一种存在类型的形式。但是,您可以定义通用类型IComponents<K>
,该类型代表具有特定键列表K
的有效组件列表,这将允许您定义类型参数K
中通用的函数。并接受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'
},
});