Let’s do some evening snacks on TypeScript’s type inference strategy


As those familiar with TypeScript know, TS has a type inference system to help reduce unnecessary type declarations. Even with pure JS code, TS can automatically give each variable a default type through the type inference system.

Do you know the type inference results of the following four quantities A, B, C. prop and D.prop?

const a = 'a';

let b = 'a';

const c = {
  prop: 'a'
};

class D {
  static prop = 'a'
}
Copy the code

They all look the same? What happens if you use them to assign values to the following variables?

type Abc = 'a' | 'b' | 'c';

const a1: Abc = a;

const b1: Abc = b; // error!

const c1: Abc = c.prop; // error!

const d1: Abc = D.prop; // error!
Copy the code

You can find that the assignment statement except a1, all the other assignment statements are wrong!

The reason is that, except for variable A, which is inferred as single-valued type ‘a’, b, c.prop, and d. prop are inferred as strings

From the range of types string > Abc > ‘a’

So the assignment of a1 is true, and the other assignments are false.

Why is TS’s inference different for seemingly similar cases?

The root reason is that TS gives different type inferences depending on whether a value is likely to be modified in subsequent logic:

  • To have aSubject to modificationTS adopts a looser type inference strategy, that is, the values described aboveb.c.prop.D.propIt is inferred to be relatively broadstringType, which gives greater flexibility to possible future assignments
  • forIt cannot be reassignedTS uses a more rigorous type inference strategy, namely the aboveaInferred to be single-valued'a'So that the futureaThere is less chance of type checking errors when assigning to other variables

Different starting points and situations result in two different types of inference strategies.

Finally, with the above strategy understood, let’s make a change to D to verify the inference logic of TS:

class D2 {
  static readonly prop = 'a';
}

const d2: Abc = D2.prop; // No more error this time!
Copy the code

The addition of the readonly keyword indicates that the prop field is immutable, triggering a stricter inference policy that allows D.prop to be inferred as a single-valued type ‘A’

Further reading

Improved Inference for Literal Types in TypeScript