我正在为表编写TypeScript接口:
interface Column {
id: string;
label: string;
}
interface Data {
[key: string]: string;
}
interface Table {
columns: Column[];
data: Data[];
}
我想限制Column.id
的允许值:每个Column.id
必须具有匹配的Data.key
。 (但是:不是,每个Data.key
必须具有匹配的Column.id
)
应允许,因为每个Column.id
都有一个匹配的Data.key
。
columns: [
{ id: 'foo', label: 'foo' },
{ id: 'bar', label: 'bar' }
]
data: [
{ foo: '123', bar: '456', baz: '789' }
]
但这应该不允许,因为Data[foo]
不存在。
columns: [
{ id: 'foo', label: 'foo' }
]
data: [
{ bar: '456' }
]
如何编写一个Table
接口来应用这些约束?
答案 0 :(得分:2)
您无法创建表示此约束的具体类型,但是您可以 使用https://codepen.io/adrianolopes17/pen/WKGjYo和辅助函数来推断强制实施该约束的通用类型。
让我们扩展Column
,Data
和Table
的定义,使其在我们关心的字符串属性中通用:
interface Column<K extends string = string> {
id: K,
label: string;
}
type Data<K extends string = string> = Record<K, string>
interface Table<K extends string = string, L extends K = K> {
columns: Column<L>[];
data: Data<K>[];
}
请注意,假设Table<K, L>
和K
是generics的并集,并且L
尽可能的狭窄,有效的K
表示你想要的约束。从L extends K
开始,这意味着columns['id']
必须是keyof data
的子类型。
以下帮助器函数将为您进行推断:
const asTable = <K extends string, L extends K>(x: Table<K, L>) => x;
好的,让我们看看它是否有效:
// no error
const goodTable = asTable({
columns: [
{ id: 'foo', label: 'foo' },
{ id: 'bar', label: 'bar' }
],
data: [
{ foo: '123', bar: '456', baz: '789' }
]
})
// error ... Type '"foo"' is not assignable to type '"bar"'
const badTable = asTable({
columns: [
{ id: 'foo', label: 'foo' }
]
,
data: [
{ bar: '456' }
]
})
看起来不错。希望有帮助!
答案 1 :(得分:0)
这是另一个可行的解决方案。
我不确定与@jcalz版本相比有什么优缺点。
也许有人可以发表评论并解释差异:)
interface Column<K extends string> {
id: K;
label: string;
}
interface Props<D extends {[key: string]: string}> {
columns: Array<Column<keyof D & string>>;
data: D[];
}