是否可以声明一个接受超类实例数组并返回最具体的子类类型的函数

2023-12-08

我正在为我的 Javascript 包编写 Typescript 声明文件。

我的库有一个函数,它接受超类元素数组并返回超类。

function x(args: SuperClass[]): SuperClass

我想修改该方法的返回类型,以便它返回最具体的子类元素的类型。

function x(args: SuperClass[]): TypeOfMostSpecificArg(args)

例如

interface A {}
interface B extends A {}
interface C extends B {}
let a: A, b: B, c: C
x([]) // should have return type A
x([a, b, a]) // should have return type B
x([a, b, c]) // should have return type C

这可能吗?


(以下使用TS3.1:)

这个答案充满了警告,以至于我即使发布它也感到很奇怪。另外,我真的不认为我理解你的用例。但是在列表中查找最具体的类型所涉及的类型杂耍激起了我的好奇心。所以我们来了!

以下可以只可能工作如果你传递给的对象x至少包含一个元素instanceof传递的每个其他值的构造函数。这意味着类层次结构中没有分叉(或者至少在您传递给的事物列表中没有分叉)x)并且它是使用原型继承的实际类层次结构。

开始:

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];
function x<T extends any[]>(...args: T): Absorb<T> extends never ? undefined : Absorb<T>;
function x(...args: any[]): any {
    return args.find(a => (args.every(b => a instanceof b.constructor)));
}

解释有点复杂,因为它使用了很多条件类型, 尤其分散式允许您检查工会成员的那些。效果是这样的Absorb<>接受一个数组(或tuple) 类型并返回所有其他元素的子类型的元素(如果有的话)...否则它将成为底部类型never.

In the x函数我还使用剩余参数而不是数组,因为它有帮助推断元组类型对于传入的参数。

让我们看看它是否有效:

class A { a: string = "a" }
class B extends A { b: string = "b" }
class C extends B { c: string = "c" }
let a = new A();
let b = new B();
let c = new C();

const aaa = x(a, a, a); // type is A, returns a at runtime
const aba = x(a, b, a); // type is B, returns b at runtime
const abc = x(a, b, c); // type is C, returns c at runtime

我认为看起来不错。

现在,这不起作用:

const none = x(); // type is never, returns undefined at runtime

我知道你想要这样A,但你没有给它任何参数。它如何返回类型的值A当它没有的时候?哦,好吧,我们假设有一个名为a在外部范围中定义。您可以修改上面的内容来制作零参数x() work:

function otherX<T extends A[]>(...args: T): Absorb<T> extends never ? A : Absorb<T>;
function otherX(...args: A[]): A {
    return args.find(z => (args.every(b => z instanceof b.constructor))) || a;
}

const none = otherX(); // type is A, returns a at runtime
const otherAba = otherX(a, b, a); // type is B, returns B at runtime
const otherAbc = otherX(a, b, c); // type is C, returns C at runtime

这里有一些警告......如果您使用带分叉的层次结构:

class D extends A { d: string = "d" }
let d = new D();
const whoops = x(a, b, c, d); // type is undefined, returns undefined at runtime
const alsoWhoops = otherX(b, c, d); // type is A, returns a at runtime

如果您使用非类实例:

const huh = x("a","b","c"); // type is supposedly string, returns undefined at runtime

也许其他疯狂的事情也可能发生。但这是我能得到的最接近的结果。希望对您有帮助。祝你好运!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

是否可以声明一个接受超类实例数组并返回最具体的子类类型的函数 的相关文章

随机推荐