作为作业项目的一部分,我正在用Typescript构建学校的Linq框架。关于Select
方法,我遇到了问题。我向Select
传递了一个属性数组,但是问题是Typescript允许数组中有重复值。在Typescript或Javascript中,有没有一种方法可以只包含唯一值的数组?
现在Select
方法看起来像这样:
在界面中:
Select: <K extends keyof T>(this: Table<T, U>, ...properties: K[]) => Table<Omit<T, K>, Pick<T, K> & U>
实现:
Select: function <K extends keyof T>(this: Table<T, U>, ...properties: K[]): Table<Omit<T, K>, Pick<T, K> & U> {
let selection = this.data.First.map(entry => pickMany(entry, properties))
let result = this.data.map(
first => first.map(entry => omitMany(entry, properties))
,
second => merge_list_types(second.zip(selection))
)
return Table(result)
}
现在可以说:
customers.Select("name", "name", "age")
如预期的那样,上面的代码将给我键入错误,因为已经选择了'name'
。
答案 0 :(得分:2)
您不能以一般的方式确保数组的唯一性(至少不能以受支持的一般方式来确保,您可以使用递归类型别名来做一些疯狂的事情,但是它们很可能会从一个版本过渡到另一个版本)。
我们可以创建一个条件类型,以确保最多多个元素的唯一性,并根据需要添加更多条件。
type Omit<T, K extends PropertyKey> = Pick<T, Extract<keyof T, K>>
type IsUnique<T extends any[]> = UK0<T, "Items are not unique", {}>
type Tail<T extends any[]> = ((...a: T) => void) extends (p: any, ...t: infer P) => void ? P : [];
type UK0<T extends any[], TErr, TOk> = T extends [] ? TOk : T[0] extends Tail<T>[number] ? TErr : UK1<Tail<T>, TErr, TOk>
type UK1<T extends any[], TErr, TOk> = T extends [] ? TOk : T[0] extends Tail<T>[number] ? TErr : UK2<Tail<T>, TErr, TOk>
type UK2<T extends any[], TErr, TOk> = T extends [] ? TOk : T[0] extends Tail<T>[number] ? TErr : UK3<Tail<T>, TErr, TOk>
type UK3<T extends any[], TErr, TOk> = T extends [] ? TOk : T[0] extends Tail<T>[number] ? TErr : UK4<Tail<T>, TErr, TOk>
type UK4<T extends any[], TErr, TOk> = T extends [] ? TOk : T[0] extends Tail<T>[number] ? TErr : UK5<Tail<T>, TErr, TOk>
type UK5<T extends any[], TErr, TOk> = T extends [] ? TOk : T[0] extends Tail<T>[number] ? TErr : UK6<Tail<T>, TErr, TOk>
type UK6<T extends any[], TErr, TOk> = T extends [] ? TOk : T[0] extends Tail<T>[number] ? TErr : "Array to big"
class Table<T, U> {
Select = function <K extends Array<keyof T>>(this: Table<T, U>, ...properties: K & IsUnique<K>): Table<Omit<T, K[number]>, Pick<T, K[number]> & U> {
return null!;
}
}
new Table<{ a: 0, b: 0}, {}>().Select("a") //ok
new Table<{ a: 0, b: 0}, {}>().Select("a", "a") // err
new Table<{ a: 0, b: 0}, {}>().Select("a", "b") //ok
一种更好的方法实际上是使用对象类型,因为对象本质上不允许键重复。
class Table<T, U> {
Select = function <K extends Partial<Record<keyof T, true>>>(this: Table<T, U>, properties: K): Table<Omit<T, keyof K>, Pick<T, keyof K> & U> {
return null!;
}
}
new Table<{ a: 0, b: 0}, {}>().Select({ a: true }) //ok
new Table<{ a: 0, b: 0}, {}>().Select({ a: true, a: true }) // err
new Table<{ a: 0, b: 0}, {}>().Select({ a: true, b: true }) //ok