是否可以声明一个接受给定类型的超类的函数? [英] Is it possible to declare a function that accepts superclasses of a given type?

查看:24
本文介绍了是否可以声明一个接受给定类型的超类的函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为 Javascript 库编写类型声明.>

我有一个在类型 T 上泛型的接口.它有一个函数,其参数应该具有类型 (T | subclass(T) | superclass(T)).

当前界面如下所示:

接口查询{扩展(参数:T):T}

按原样,Query.extend 只接受 T 和 T 的子类.如何添加 T 的超类?

上下文:

Sqorn Postgres 是一个具有流畅界面的不可变 SQL 查询生成器.其API基于SQL,具有from()insert()limit()等方法

根查询构建对象具有所有可用的方法,但是当您链接方法时,您限制了查询类型并且可用的方法更少.例如,sq.insert().sharing() 会抛出一个类型错误,因为 sq.insert() 返回了一个 InsertBuilder 和 Insert Queries没有 have 子句.

为了可组合性,您可以链接方法 extend(...queries: AnyBuilder[]) 以从现有查询创建新查询.

sq.from('book').extend(sq.where({ id: 1 }), sq.return('name'))

上面方法调用中的类型是:

SelectInsertUpdateDeleteBuilder.extend(选择更新删除构建器,选择插入更新删除构建器)

我想为 extend() 添加类型安全性,这样如果 T 是当前查询构建器类型,则只接受类型 T、T 的超类和 T 的子类的查询.

例如,这会阻止您使用仅在选择查询中有意义的方法扩展插入查询,例如

sq.insert().extend(sq.limit())//应该抛出类型错误

上面方法调用中的类型是:

InsertBuilder.extend(SelectBuilder)

解决方案

我可以让 Query.extend() 只接受可分配给 T 的参数 或那些 T 可分配给的.也就是说,要么是超类型,要么是子类型.它使用(正如这些事情经常做的那样)条件类型:

接口查询{扩展(x:U):U;}

让我们试试吧:

class A { a: string = "a" }B 类扩展 A { b: string = "b" }类 C 扩展 B { c: string = "c" }D 类扩展 A { d: string = "d" }类 E { e: 字符串 = "e" }声明 let a: A;声明 let b: B;声明 let c: C;声明 let d: D;声明 let e: E;声明 let qb: Query;qb.extend(a);//好的,B 扩展了 Aqb.extend(b);//好的,B 扩展了 Bqb.extend(c);//好的,C 扩展了 Bqb.extend(d);//错误,D 不能赋值给 Bqb.extend(e);//错误,E 不能赋值给 Bqb.extend(Math.random() <0.5 ? a : e);//好的,B 扩展了 A |乙

对我来说看起来很合理.希望这对你有用.祝你好运!

<小时>

更新:将此与我对另一个问题的回答结合起来,关于接受多个参数并返回最具体的一个",加上那个问题的所有警告和疯狂,给你:

type NotExtendsAll= (U extends any ? [T] extends [U] ? never : unknown : never)类型 AbsorbUnion<T>= [T] 扩展 [推断 U] ?你扩展任何?NotExtendsAll从不延伸?U:从不:从不:从不类型 Absorb= AbsorbUnion<{[K in keyof T]: [T[K]] }[number]>[0];类型 AsArray= [T] 扩展 [any[]] ?T : 从不;接口查询{extend>(...x:你):吸收<U>从不延伸?T:吸收U;}

基本上我在那里做的是要求 U 是一个数组或类型元组,其中每个类型都是 T 的子类型或超类型,它返回最窄参数的类型,如果没有最窄参数(零参数或分叉类层次结构中的多个参数),则为 T.

以上所有测试都给出了相同的结果,现在您也可以这样做:

qb.extend(a, a, a);//好的,返回类型 Aqb.extend(b, a, b, b, a);//好的,返回类型 Bqb.extend(a, b, c, b, a);//好的,返回类型 Cqb.extend();//好的,返回类型 B

不确定Query的实现;希望你能解决这个问题.

再次祝你好运!

I'm writing type declarations for a Javascript library.

I have an interface that is generic on type T. It has a function with an argument that should have type (T | subclass(T) | superclass(T)).

The interface currently looks like the following:

interface Query<T> {
  extend(arg: T): T
}

As is, Query.extend only accepts T and subclasses of T. How do I add superclasses of T?

Context:

Sqorn Postgres is an immutable SQL Query Builder with a fluent interface. Its API is based on SQL and features methods like from(), insert(), limit(), etc.

The root query building object has all methods available to it, but as you chain methods, you constrain the query type and fewer methods become available. For example, sq.insert().having() will throw a type error because sq.insert() returned an InsertBuilder and Insert Queries don't have a having clause.

For composability, you can chain method extend(...queries: AnyBuilder[]) to create a new query from existing queries.

sq.from('book').extend(sq.where({ id: 1 }), sq.return('name'))

The types in the method call above are:

SelectInsertUpdateDeleteBuilder.extend(
  SelectUpdateDeleteBuilder,
  SelectInsertUpdateDeleteBuilder
)

I'd like to add type safety to extend() such that if T is the current query builder type, only queries of type T, superclasses of T, and subclasses of T are accepted.

This would, for example, prevent you from extending an Insert Query with methods that only make sense in a Select Query, e.g.

sq.insert().extend(sq.limit()) // should throw type error

The types in the method call above are:

InsertBuilder.extend(SelectBuilder)

解决方案

I can make it so that Query<T>.extend() only takes parameters that are either assignable to T or those that T is assignable to. That is, either a supertype or a subtype. It uses (as these things often do) conditional types:

interface Query<T> {
  extend<U extends (T extends U ? unknown : T)>(x: U): U;
}

Let's try it:

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

Looks reasonable to me. Hope that's useful to you. Good luck!


UPDATE: combining this with my answer to the other question about accepting a number of arguments and returning "the most specific one", with all the caveats and craziness from that question, gives you:

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>;
}

Basically what I'm doing there is requiring U to be an array or tuple of types where each type is either a subtype or supertype of T, and it returns the type of the narrowest argument, or just T if there is no narrowest argument (zero arguments, or multiple arguments in a forked class hierarchy).

All the above tests give the same results, and now you can also do this:

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

Not sure about the implementation of Query; hopefully you can handle that.

Good luck again!

这篇关于是否可以声明一个接受给定类型的超类的函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆