我正在为Javascript库编写type declarations。
我有一个类型T通用的接口。它具有一个函数,该函数的参数应为类型(T | subclass(T)| superclass(T))。
该界面当前如下所示:
interface Query<T> {
extend(arg: T): T
}
按原样,Query.extend仅接受T和T的子类。如何添加T的超类?
上下文:
Sqorn Postgres是具有流畅界面的不可变SQL查询生成器。其API基于SQL,并具有from()
,insert()
,limit()
等方法。
根查询构建对象具有所有可用的方法,但是在链接方法时,您将约束查询类型,并且可用方法更少。例如,sq.insert().having()
将引发类型错误,因为sq.insert()
返回了InsertBuilder
,并且插入查询没有having
子句。
对于可组合性,您可以链接方法extend(...queries: AnyBuilder[])
来从现有查询中创建新查询。
sq.from('book').extend(sq.where({ id: 1 }), sq.return('name'))
上面的方法调用中的类型是:
SelectInsertUpdateDeleteBuilder.extend(
SelectUpdateDeleteBuilder,
SelectInsertUpdateDeleteBuilder
)
我想在extend()
中添加类型安全性,以便如果T是当前的查询构建器类型,则仅接受类型T,T的超类和T的子类的查询。
例如,这将阻止您使用仅在“选择查询”中有意义的方法来扩展“插入查询”。
sq.insert().extend(sq.limit()) // should throw type error
上面的方法调用中的类型是:
InsertBuilder.extend(SelectBuilder)
答案 0 :(得分:3)
我可以做到,使得Query<T>.extend()
仅采用可分配给T
或可分配给T
的参数。即,超类型或子类型。它使用(就像这些事情经常做的那样)conditional types:
interface Query<T> {
extend<U extends (T extends U ? unknown : T)>(x: U): U;
}
让我们尝试一下:
class A { a: string = "a" }
class B extends A { b: string = "b" }
class C extends B { c: string = "c" }
class D extends A { d: string = "d" }
class E { e: string = "e" }
declare let a: A;
declare let b: B;
declare let c: C;
declare let d: D;
declare let e: E;
declare let qb: Query<B>;
qb.extend(a); // okay, B extends A
qb.extend(b); // okay, B extends B
qb.extend(c); // okay, C extends B
qb.extend(d); // error, D not assignable to B
qb.extend(e); // error, E not assignable to B
qb.extend(Math.random() < 0.5 ? a : e); // okay, B extends A | E
我觉得合理。希望对您有用。祝你好运!
更新:将其与my answer to the other question结合使用,以接受许多参数并返回“最具体的参数”,以及该问题的所有警告和疯狂之处,为您提供:
type NotExtendsAll<T, U> = (U extends any ? [T] extends [U] ? never : unknown : never)
type AbsorbUnion<T> = [T] extends [infer U] ? U extends any ?
NotExtendsAll<U, T> extends never ? U : never : never : never
type Absorb<T extends any[]> = AbsorbUnion<{ [K in keyof T]: [T[K]] }[number]>[0];
type AsArray<T> = [T] extends [any[]] ? T : never;
interface Query<T> {
extend<U extends AsArray<{ [K in keyof U]: T extends U[K] ? unknown : T }>>(
...x: U
): Absorb<U> extends never ? T : Absorb<U>;
}
基本上,我正在做的工作是要求U
是类型的数组或元组,其中每个类型都是T
的子类型或超类型,并且它返回最窄参数的类型,或者如果没有最窄的参数(零个参数,或者派生的类层次结构中有多个参数),则只是T
。
以上所有测试均给出相同的结果,现在您也可以这样做:
qb.extend(a, a, a); // okay, returns type A
qb.extend(b, a, b, b, a); // okay, returns type B
qb.extend(a, b, c, b, a); // okay, returns type C
qb.extend(); // okay, returns type B
不确定Query
的实现;希望你能解决。
祝你好运!