0

Say I have

class Foo {}
class Bar {}

class Query {
  construct(...args: any[]) {
    // snip
  }

  result() {
    // snip
  }
}

Is it possible in typescript to say that given:

const query = new Query(Foo, Bar);

that query.result() will have return type [Foo, Bar] ?

2
  • 1
    query = Query(Foo, Bar) -- used this way, Query is not a constructor but a regular function. In order to be a constructor it has to be used using new. From the OOP point of view, an object is a smart component, not just a dumb container. If the constructor accepts any arguments then your "class" is not a class, it is just a convoluted way to implement a dumb container. Commented Nov 3, 2020 at 18:56
  • @axiac typo fixed. Commented Nov 3, 2020 at 18:56

2 Answers 2

1

You could use Generics for that:

class Foo{}
class Bar{}

class Query<T> {
    constructor(private arg: T) {
    }
    
    query (): T {
        return this.arg;
    }
}

const q = new Query<[Foo, Bar]>([new Foo(), new Bar()]);
const q.query() // typehints [Foo, Bar] as returntype
Sign up to request clarification or add additional context in comments.

2 Comments

I hadn't considered trying to lump it into one generic parameter. I think it would have to be Query<T extends []> , ...args: T or similar since I need to know given arg[0] was Foo, response[0] will be instanceof Foo, etc. I will see if I can get some flavor of this to work. Thanks for your input!
if my answer helped you. I would be happy if you mark it as accepted answere :D
0

For my specific use case (transforming variables within an Array) I ended up having to use a more sophisticated approach, using the techniques described here: https://medium.com/free-code-camp/typescript-curry-ramda-types-f747e99744ab

/* eslint-disable @typescript-eslint/no-explicit-any */
type Length<T extends any[]> = T['length']

type Cast<X, Y> = X extends Y ? X : Y;

type Prepend<E, T extends any[]> =
  ((head: E, ...args: T) => any) extends ((...args: infer U) => any)
    ? U
    : T

type Pos<I extends any[]> =
  Length<I>;

type Next<I extends any[]> =
  Prepend<any, I>;

type Reverse<
  T extends any[],
  R extends any[] = [],
  I extends any[] = []
> = {
  0: Reverse<T, Prepend<T[Pos<I>], R>, Next<I>>
  1: R
}[
  Pos<I> extends Length<T>
    ? 1
    : 0
]

type Concat<T1 extends any[], T2 extends any[]> =
  Reverse<Reverse<T1> extends infer R ? Cast<R, any[]> : never, T2>;

type Append<E, T extends any[]> =
  Concat<T, [E]>;

export type InstanceTypes<
  T extends { new(...args: any): any}[],
  R extends any[] = [],
  I extends any[] = []
> = {
  0: InstanceTypes<
    T,
    Append<InstanceType<T[Pos<I>]>, R> extends infer U
      ? Cast<U, any[]>
      : never,
    Next<I>
  >
  1: R
}[
  Pos<I> extends Length<T>
    ? 1
    : 0
];

InstanceTypes correctly converts [typeof A, typeof B, ...] to [A, B, C] as I require.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.