TypeScript:从字符串联合文字数组类型开始,禁止数组中存在任何冗余值

问题描述

我正在使用许多文字字符串联合类型,以及它们的数组以用于与定义sql表/视图及其所有列有关的一些代码

请参见下面的示例代码,其中有一个示例user sql表,该表具有以下列:idusernameemailpassword ...

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个项目,则2 N + 1个元组。 Fabian链接的答案在某些情况下会更好,但这将为其他情况提供明显更好的自动完成功能。 Playground link