是否可以声明一个函数来接受给定类型的超类?

时间:2018-10-25 20:40:42

标签: typescript typescript-typings

我正在为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)

1 个答案:

答案 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的实现;希望你能解决。

祝你好运!