推断对象数组中特定键的值

死神

我想编写一个非常聪明的DataTable类

export interface ColumnDefinition<?> {
  readonly name: ?;
  title: string;
  align?: 'left' | 'center' | 'right';
  sortable?: boolean;
  hideable?: boolean;
  // ...
}

export interface DataTableOptions<?> {
  readonly columnDefinitions: ColumnDefinition<?>[];
  // ...
}

export class DataTable<?> {
  private readonly columnDefinitions: ReadonlyArray<ColumnDefinition>;

  constructor(options: DataTableOptions<?>) {
    this.columnDefinitions = options.columnDefinitions;
    // ...
  }

  // ...

  public title(columnName: ?): string {
    return this.columnDefinitions.find(({ name }) => name === columnName)?.title ?? '';
  }

  // ...
}

我把一些?放在我不知道如何提供泛型类型的地方

目标是调用以下内容

const table = new DataTable({
  columnDefinitions: [
    { name: 'id', title: 'ID' },
    { name: 'v1', title: 'Value 1' },
    { name: 'v2', title: 'Value 2' }
  ],
  // ...
});

然后,表的键入应如下所示:DataTable<'id' | 'v1' | 'v2'>
如果有人尝试使用具有相同名称的多个列定义,则会发生错误

此外,一些成员职能也应从中受益

table.title('id'); // 'ID'
table.title('test'); // compile time error
提香·切尔尼科娃·德拉戈米尔

确保我们捕获传递的实际名称很容易。我们将以元组类型捕获整个列定义(这在尝试测试唯一性时也将为我们提供帮助)

要捕获columnDefinitions作为元组类型的类型,我们需要一个type参数,让其C以约束进行调用[ColumnDefinition] | ColumnDefinition[]第一个[ColumnDefinition]确保我们得到一个元组类型,而ColumnDefinition[]确保我们允许任何类型的元组。

为了捕获每个属性的名称,我们需要做更多的事情。首先ColumnDefinition需要一个类型参数扩展stringinterface ColumnDefinition<N extends string>{...})。这将使我们能够编写类似ColumnDefinition<'id'>

有可能保留名称的字符串文字类型,ColumnDefinition我们需要回到的约束C现在,我们需要在约束中指定类型参数。[ColumnDefinition<string>] | ColumnDefinition<string>[]是有效的,但不会捕获字符串文字类型,只会推断出string元组中的所有项目。为了获得字符串文字类型,我们需要将一个额外的类型参数限制为string(这将触发编译器保留字符串文字类型。

所以最终的定义DataTableclass DataTable<C extends [ColumnDefinition<V>] | ColumnDefinition<V>[], V extends string> {... }

有了C类型后,我们可以输入title相对于的参数C所以我们可以写title(columnName: C[number]['name']): string

唯一性部分难以保证。我们将需要一个递归条件类型(附加了各种警告)。但这是可以完成的。如果没有重复项或包含自定义错误消息的元组,则IsUnique类型下面将返回{},这将在调用构造函数时导致错误。

产生的解决方案:

export interface ColumnDefinition<N extends string> {
    readonly name: N;
    title: string;
    align?: 'left' | 'center' | 'right';
    sortable?: boolean;
    hideable?: boolean;
    // ...
}

export interface DataTableOptions<C extends ColumnDefinition<string>[]> {
    readonly columnDefinitions: C;
    // ...
}

type ColumnName<T> = T extends ColumnDefinition<infer N> ? N : never;
type IsUnique<T extends any[], E = never> = {
    next: ((...a: T) => void) extends ((h: infer H, ...t: infer R) => void) ?
        [ColumnName<H>] extends [E] ? ["Names are not unique", ColumnName<H>, "was found twice"] :
        IsUnique<R, E | ColumnName<H>>: ["NO", T]
    stop: {}
}[T extends [] ? "stop" : "next"];


export class DataTable<C extends [ColumnDefinition<V>] | ColumnDefinition<V>[], V extends string> {
    private readonly columnDefinitions: Readonly<C>;

    constructor(options: DataTableOptions<C>  & IsUnique<C> ) {
        this.columnDefinitions = options.columnDefinitions;
        // ...
    }

    public title(columnName: C[number]['name']): string {
        return this.columnDefinitions.find(({ name }) => name === columnName)?.title ?? '';
    }

    // ...
}

const table = new DataTable({
    columnDefinitions: [
        { name: 'id', title: 'ID' },
        { name: 'v1', title: 'Value 1', align: "right" },
        { name: 'v2', title: 'Value 2', align: "right" }
        // { name: 'id', title: 'ID' }, 
        // Comment the line above to get the error below
        // Type '{ columnDefinitions: [{ name: "id"; title: string; }, { name: "v1"; title: string; }, { name: "v2"; title: string; }, { name: "id"; title: string; }]; }' is not assignable to type '["Names are not unique", "id", "was found twice"]'.(2345)
    ],
    // ...
});
table.title("Id") // err
table.title("id") // ok


游乐场链接

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

从数组中删除具有特定“键:值”的对象

在对象数组或对象对象中查找特定键的所有值

如何从对象数组中获取具有特定键唯一值的对象?

如何从JSON对象数组中删除具有特定键/值对的所有JSON对象?

如何过滤对象数组并检查特定键是否在数组中具有值

如何在不迭代数组的情况下提取对象数组中特定键的值?

如何从条件键推断对象的值?

从对象和键数组中查找值

键数组,在json对象中查找值

Javascript中对象与数组的键/值对

提取对象数组中未知键的值

在合并对象的键中创建值数组

获取对象数组中重复键的值

从对象数组中删除键和值

在嵌套对象中搜索特定键的值

将对象数组转换为特定键的值数组

如何基于Postgres中的特定键/值对更新JSON数组中的对象

我想从java脚本中的对象数组中删除特定的键和值

如何从对象中的所有键获取特定值并将其存储在数组中?

从哈希数组中获取特定键的值

如何根据对象对象中的键对值过滤对象数组?

使用 jq,获取特定键的值,该键位于数组中的 shell 定义的 JSON 对象内

如何在嵌套对象数组中查找特定键的所有值?

从 JSONPath 中的不同数组对象查询具有特定值的键

通过特定键的值在多个数组中查找对象

如何根据对象数组中特定键的值来创建单位类型?

在Ruby 2.0中使用特定键和值从数组构建JSON对象

检索具有特定值的对象数组中的所有键

如何将对象中特定键的值存储到数组-Vue