Is it possible to declare a function that accepts superclasses of a given type?

后端 未结 1 382
时光取名叫无心
时光取名叫无心 2021-01-15 21:31

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 |

1条回答
  •  隐瞒了意图╮
    2021-01-15 22:02

    I can make it so that Query.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 {
      extend(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;
    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 = (U extends any ? [T] extends [U] ? never : unknown : never)
    type AbsorbUnion = [T] extends [infer U] ? U extends any ?
      NotExtendsAll extends never ? U : never : never : never
    type Absorb = AbsorbUnion<{ [K in keyof T]: [T[K]] }[number]>[0];
    
    type AsArray = [T] extends [any[]] ? T : never;    
    interface Query {
      extend>(
      ...x: U
      ): Absorb extends never ? T : Absorb;
    }
    

    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!

    0 讨论(0)
提交回复
热议问题