Improved inference for generics

TypeScript 2.4 introduces a few wonderful changes around the way generics are inferred.

Return types as inference targets

For one, TypeScript can now make inferences for the return type of a call.This can improve your experience and catch errors.Something that now works:

  1. function arrayMap<T, U>(f: (x: T) => U): (a: T[]) => U[] {
  2. return a => a.map(f);
  3. }
  4. const lengths: (a: string[]) => number[] = arrayMap(s => s.length);

As an example of new errors you might spot as a result:

  1. let x: Promise<string> = new Promise(resolve => {
  2. resolve(10);
  3. // ~~ Error!
  4. });

Type parameter inference from contextual types

Prior to TypeScript 2.4, in the following example

  1. let f: <T>(x: T) => T = y => y;

y would have the type any.This meant the program would type-check, but you could technically do anything with y, such as the following:

  1. let f: <T>(x: T) => T = y => y() + y.foo.bar;

That last example isn’t actually type-safe.

In TypeScript 2.4, the function on the right side implicitly gains type parameters, and y is inferred to have the type of that type-parameter.

If you use y in a way that the type parameter’s constraint doesn’t support, you’ll correctly get an error.In this case, the constraint of T was (implicitly) {}, so the last example will appropriately fail.

Stricter checking for generic functions

TypeScript now tries to unify type parameters when comparing two single-signature types.As a result, you’ll get stricter checks when relating two generic signatures, and may catch some bugs.

  1. type A = <T, U>(x: T, y: U) => [T, U];
  2. type B = <S>(x: S, y: S) => [S, S];
  3. function f(a: A, b: B) {
  4. a = b; // Error
  5. b = a; // Ok
  6. }