Higher order type inference from generic constructors

In TypeScript 3.4, we improved inference for when generic functions that return functions like so:

  1. function compose<T, U, V>(
  2. f: (x: T) => U, g: (y: U) => V): (x: T) => V {
  3. return x => g(f(x))
  4. }

took other generic functions as arguments, like so:

  1. function arrayify<T>(x: T): T[] {
  2. return [x];
  3. }
  4. type Box<U> = { value: U }
  5. function boxify<U>(y: U): Box<U> {
  6. return { value: y };
  7. }
  8. let newFn = compose(arrayify, boxify);

Instead of a relatively useless type like (x: {}) => Box<{}[]>, which older versions of the language would infer, TypeScript 3.4’s inference allows newFn to be generic.Its new type is <T>(x: T) => Box<T[]>.

TypeScript 3.5 generalizes this behavior to work on constructor functions as well.

  1. class Box<T> {
  2. kind: "box";
  3. value: T;
  4. constructor(value: T) {
  5. this.value = value;
  6. }
  7. }
  8. class Bag<U> {
  9. kind: "bag";
  10. value: U;
  11. constructor(value: U) {
  12. this.value = value;
  13. }
  14. }
  15. function composeCtor<T, U, V>(
  16. F: new (x: T) => U, G: new (y: U) => V): (x: T) => V {
  17. return x => new G(new F(x))
  18. }
  19. let f = composeCtor(Box, Bag); // has type '<T>(x: T) => Bag<Box<T>>'
  20. let a = f(1024); // has type 'Bag<Box<number>>'

In addition to compositional patterns like the above, this new inference on generic constructors means that functions that operate on class components in certain UI libraries like React can more correctly operate on generic class components.

  1. type ComponentClass<P> = new (props: P) => Component<P>;
  2. declare class Component<P> {
  3. props: P;
  4. constructor(props: P);
  5. }
  6. declare function myHoc<P>(C: ComponentClass<P>): ComponentClass<P>;
  7. type NestedProps<T> = { foo: number, stuff: T };
  8. declare class GenericComponent<T> extends Component<NestedProps<T>> {
  9. }
  10. // type is 'new <T>(props: NestedProps<T>) => Component<NestedProps<T>>'
  11. const GenericComponent2 = myHoc(GenericComponent);

To learn more, check out the original pull request on GitHub.