TypeScript泛型column.id应该在row [key]之内

时间:2018-07-18 19:28:08

标签: typescript generics typescript-generics

我正在为表编写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接口来应用这些约束?

2 个答案:

答案 0 :(得分:2)

您无法创建表示此约束的具体类型,但是您可以 使用https://codepen.io/adrianolopes17/pen/WKGjYo和辅助函数来推断强制实施该约束的通用类型。

让我们扩展ColumnDataTable的定义,使其在我们关心的字符串属性中通用:

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>Kgenerics的并集,并且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[];
}