TypeScript:来自字符串联合文字数组类型,不允许数组中存在任何冗余值

TypeScript: from a string union literal array type, disallow any redundant values from existing in the array

我使用了很多文字字符串联合类型,以及它们的数组用于一些与定义 SQL tables/views 及其所有列相关的代码。

请参阅下面的示例代码,其中我们有一个示例 user SQL table,其中包含以下列:idusernameemail, password..

export type UserTableColumnName = 'id' | 'username' | 'email' | 'password';
export type ArrayOfUserTableColumns = UserTableColumnName[]; // This allows for redundant values, but I don't want it to allow them

function choose_some_user_table_columns(chosen_columns: ArrayOfUserTableColumns) {
    // function code not important
}

/**
 * This is fine, no error should occur:
 */
choose_some_user_table_columns(['id', 'email']);

/**
 * I want the code below to trigger a TypeScript typing error due to the 'email' element value being given twice:
 */
choose_some_user_table_columns(['id', 'email', 'email']);

有什么方法可以创建基于(或类似于)UserTableColumnName[] 的类型 - 但是如果多次给出相同的值,TypeScript 会触发错误吗?例如email 在上面代码示例的最后一行指定了两次。

我正在寻找 TypeScript 解决方案(而不是运行时 JS 检查)。

理想情况下,如果我的编辑器(vscode 或任何支持 TypeScript 的编辑器)只建议数组中不存在的列名,那也很棒。由于目前智能感知将自动建议每一列,无论它们是否已经在数组中。

您可以使用表示递归算法中每个步骤的映射类型来执行此操作,以生成所有允许的数组排列。 (TS 4.0+ 由于可变元组的使用,你可以在旧版本中这样做,但它变得混乱)

type UniqueItems<T extends string, U extends string[] = []> = U | {
  [K in T]: UniqueItems<Exclude<T, K>, [...U, K]>
}[T]

但是,请注意,这并不能很好地扩展。在 T 联合中有 1 个项目,你得到 2 个元组。有 2 个项目,5 个元组。对于 N 项,2N + 1 个元组。 Fabian 链接的答案在某些情况下会更好,但这将为其他人提供 显着 更好的自动完成。 Playground link.