5

I've got a situation where I want to use a generic type in a set of interfaces. My public interface receives the generic type and passes it down to the non-exported private interfaces. In my public interface I want to use the generic type T, but I don't want it to refer to an instance of T. I want it to say that it is T, the class, that can produce instances of T.

Trying this, I get an error:

interface Car<T> {
  unit: T;
  progress: number;
}

export interface CarFactory<T> {
  cars: Car<T>[];
  // Type error: 'T' only refers to a type, but is being used as a value here.  TS2693
  blueprint: typeof T;
}

Using a generator function works. But then I have to pass that down and expose more of the internals of my code, which I want to avoid.

interface CarFactory<T> {
  blueprint: (args: any) => T;
}

I can't use T directly, as that causes the compiler to think it should receive an instance of T, not the class. This triggers a TS2740 error. Using T = CarModel and T['constructor'] as the blueprint type works, but only if I patch my class like this:

class CarModel {
  public ['constructor']: typeof CarModel;
}

So the question is: How can I use generics like this? Using both instances of T and the actual T? Are a generator function or ['constructor'] with a patched T my only alternatives? Would I need to pass down another generic type U that is something along the lines of U = typeof T?

3
  • What is T supposed to be in Car<T>? It's some sort of Car that only works with a given type but I'm not sure if that's what you mean here. Perhaps you don't even need a generic for Car. It seems more logical to me to have interface Car without a generic and then something like interface CarFactory<T extends Car> because the CarFactory will create something that is a Car. Commented Sep 24, 2019 at 8:06
  • 1
    There is no direct link between the instance type and the class type if that is wht you are after. There is a proposal to type constructor as you have in CarModel automatically, but it is not yet implemented Commented Sep 24, 2019 at 8:07
  • I realized I got confused in converting my code to a example here. Fixed my question to be more to the point. Sorry for being unclear! Commented Sep 24, 2019 at 9:47

2 Answers 2

2

Do you mean something like this?

Given a class Car the type of the class itself (the symbol Car that references the class' constructor function) would be typeof Car. In CarFactory interface, we do not have some concrete class, so we can use the type of the constructor function new (...args: any) => T for blueprint:

export interface CarFactory<T> {
  cars: Car<T>[];
  // use a constructor function type here
  blueprint: new (...args: any) => T;
}

Test it:

class CombiUnit {
  // your implementation of a car unit/model T goes here
}

type CombiUnitFactory = CarFactory<CombiUnit>;

// new (...args: any) => CombiUnit
type CombiUnitFactoryCtorFunction = CombiUnitFactory["blueprint"];

// some concrete factory
declare const t1: CombiUnitFactory;

const result = new t1.blueprint(); // const result: CombiUnit

Playground

Sign up to request clarification or add additional context in comments.

1 Comment

Yes! Exactly like that. new (...args: any) => T was exactly what I was looking for. Thanks a lot!
0

The typeof operator effectively returns a value. It's sufficient to write

interface Car<T> {
  blueprint: T;
}

Besides that, I think that a factory class should be static, therefore doesn't require an interface. You could instead make a static car factory class like this:

export class AutomobileFactory {
  static createCar<T>(): Car<T> {
    // create a concrete car implementation of the
    // generic type T instead of throwing an error
    throw new Error("Not implemented");
  }
}

1 Comment

I updated my examples and question to be less confusing and explain the situation more direct. Thank you for your answer though! Also, to avoid creating static methods with run-time errors, you can use abstract classes and methods.

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.