代码之家  ›  专栏  ›  技术社区  ›  Benjamin M

typescript泛型column.id应在行[键]内

  •  0
  • Benjamin M  · 技术社区  · 6 年前

    我正在为表编写一个typescript接口:

    interface Column {
        id: string;
        label: string;
    }
    
    interface Data {
        [key: string]: string;
    }
    
    interface Table {
        columns: Column[];
        data: Data[];
    }
    

    我想限制 Column.id :每个 列.id 必须有匹配的 Data.key 是的。(但是: 不是 每一个 数据.key 必须有匹配的 列.id )

    示例:

    这应该是 允许 ,因为 列.id 有匹配的 数据.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 回复  |  直到 6 年前
        1
  •  2
  •   jcalz    6 年前

    不能生成表示此约束的具体类型,但是 可以 使用 generics 以及一个helper函数来推断执行该约束的泛型类型。

    让我们扩展一下 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 L string literals 还有那个 尽可能的窄,表达你想要的约束。自从 L extends K ,意思是 columns['id'] 必须是的子类型 keyof data 是的。

    下面的helper函数将为您进行推理:

    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' }
      ]
    })
    

    看起来不错。希望能有帮助!

        2
  •  0
  •   Benjamin M    6 年前

    这是另一个解决方案,似乎有效。

    我不知道与@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[];
    }