Typescript: Use typeof with generic

Created on 15 Dec 2017  ·  3Comments  ·  Source: microsoft/TypeScript

TypeScript Version: Current playground version (?)

Code

class GenericGroup<T> {
  items: Array<T> = [];
  constructor(name: string) {}
}

type CheckboxValues = string;
type CheckboxesGroup = new (name: string) => GenericGroup<CheckboxValues>;
const Checkboxes: CheckboxesGroup = GenericGroup;
const checkboxes = new Checkboxes('checkboxes');
checkboxes.items.map(item => item.toUpperCase());


type RadioValues = string;
type RadiosGroup = typeof GenericGroup<RadioValues>;
const Radios: RadiosGroup = GenericGroup;
const radios = new Radios('radios');
radios.items.map(item => item.toUpperCase());

link

Expected behavior:

typeof GenericGroup<RadioValues>; is equivalent to new (name: string) => GenericGroup<RadioValues>;

Actual behavior:

Syntax error, because typeof GenericGroup<RadioValues>; is not supported.

Motivation:

In my use case I have a class with a generic (like GenericGroup) which _extends_ a class from a 3rd party lib. The class from the 3rd party lib uses multiple params in its constructor. When I alias my class with the filled generic I don't want to write the parameters for the 3rd party lib every time (as done with new (name: string) => GenericGroup<CheckboxValues>;) as I don't really maintain them.

(There seem to be multiple related issues, but I couldn't found an issue with exactly this problem.)

Question

Most helpful comment

The 'typeof' operator is not meant to describe the constructor of a type, but rather the type of a value. SomeGeneric is not a value, it is a type in and of itself. Any other interface/type (like the one defined below) will not work in this way either.

interface MyInterface {
    name: string;
}

let m: typeof MyInterface; // error "MyInterface only refers to a type, but is being used as a value here."

A solution to this is to define a generic type to describe constructors like Angular does:

// angular/packages/core/src/type.ts

export interface Type<T> extends Function { 
    new (...args: any[]): T; 
}

from (angular/packages/core/src/type.ts)

Usage example:

type RadioValues = string;
type RadiosGroup = Type<GenericGroup<RadioValues>>;
const Radios: RadiosGroup = GenericGroup;
const radios = new Radios('radios');
radios.items.map(item => item.toUpperCase());

All 3 comments

The 'typeof' operator is not meant to describe the constructor of a type, but rather the type of a value. SomeGeneric is not a value, it is a type in and of itself. Any other interface/type (like the one defined below) will not work in this way either.

interface MyInterface {
    name: string;
}

let m: typeof MyInterface; // error "MyInterface only refers to a type, but is being used as a value here."

A solution to this is to define a generic type to describe constructors like Angular does:

// angular/packages/core/src/type.ts

export interface Type<T> extends Function { 
    new (...args: any[]): T; 
}

from (angular/packages/core/src/type.ts)

Usage example:

type RadioValues = string;
type RadiosGroup = Type<GenericGroup<RadioValues>>;
const Radios: RadiosGroup = GenericGroup;
const radios = new Radios('radios');
radios.items.map(item => item.toUpperCase());

A subtle thing is that generic class constructor functions are not contained in a generic type. You can tell that because the type parameter is not in scope for static members! It "feels" like you have a type like this

interface GenericCtor<T> {
  new(x: string): InstanceType<T>;
}

but the real type is like this

interface GenericCtor {
  new<T>(x: string): InstanceType<T>;
}

Thank you both. I need to wrap my head around this. In my case I extend from a 3rd party constructor (React.Component) so I need to figure out how to apply your feedback there. Thanks.

Was this page helpful?
0 / 5 - 0 ratings