liveBook · Manning

Type inference

As explained in Essential TypeScript 5, Third Edition

Type inference is the compiler's derivation of types for program elements from their values, expressions, and usage without explicit annotations.

Type inference lets the TypeScript compiler check code without requiring an annotation on every variable or function result. It derives types from initial values, expressions, and existing type information. An annotation on a method parameter can therefore help check a call even when the calling code has none. section 7.2.2 section 2.2.5

Follow a value through a calculation

In src/index.ts, `function calculateTax(amount: number) { return amount * 1.2; }` has an inferred `number` result. With `let price = 100`, both `calculateTax(price)` and the resulting `let taxAmount = ...` are checked as numbers; `let halfShare = taxAmount / 2` is a number too. Change the return expression to `(amount * 1.2).toFixed(2)`, and the inferred result becomes `string`. Dividing that result by 2 then produces a compiler error. JavaScript could coerce the string, but TypeScript flags the arithmetic instead. section 7.2.2

Inference also carries type information from a class definition into ordinary application code. A `TodoCollection.addTodo(task: string): number` call accepts a string and returns a number. Passing a `TodoItem` instead produces TS2345, even when the local variables in src/index.ts have no explicit annotations. Add annotations when they make the code easier to read; they are not needed at every use. section 2.2.5

Arrays depend on their initial values

`let prices = [100, 75, 42]` is inferred as `number[]`, and `let names = ["Hat", "Gloves", "Umbrella"]` as `string[]`. Those types carry into a `forEach` callback. An empty `let prices = []` gives the compiler no initial values to use. In the illustrated configuration it becomes `any[]`, allowing numbers and a string to be pushed without a type error. With `strictNullChecks: true`, the illustrated empty-array case instead infers `never[]` and rejects the push. Give an array an explicit type when it starts empty. section 9.2.1 section 9.2.3

Inference does not necessarily choose the shared superclass you intend for an array. An array initialized with two different subclasses can be inferred as a union of those subclasses; pushing a third subclass then fails. Declare the array as the superclass type when it must hold all three. The compiler responds to the types and shapes of the initial objects, not just the class hierarchy you have in mind. section 11.3.7

For conditional types, `infer` makes the inference request explicit. `type targetKeys<T> = T extends (infer U)[] ? keyof U : keyof T` obtains an array's element type before selecting its property keys. A similar conditional type uses `infer R` to obtain a function's result type. These patterns serve generic code; routine variable inference needs no `infer` keyword. section 13.6.6

When an inferred type is surprising, enable the `declaration` compiler option and inspect the emitted index.d.ts file in dist. It shows, for example, whether an array was declared as `number[]`, `any[]`, or a union of class types. section 7.2.2 section 9.2.1 section 11.3.7

How Essential TypeScript 5, Third Edition covers it

How deep it goes
Works through inferred expression and array types, then examines empty-array and subclass pitfalls and conditional-type inference.
Examples
Uses a tax calculation, a todo-list method error, initialized and empty arrays, subclass arrays, and generic property selection.
What it assumes
Assumes familiarity with JavaScript values and arithmetic while explaining how the compiler derives static types from them.
What you can do after
Diagnose/debug with it by checking inferred types in declaration files and adding annotations where inference permits errors or excludes intended values.
How it connects
Connects static types with type annotations, arrays, class inheritance, conditional types, and type declaration files.

Also explained in

Compare how 16 books explain type inference →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Essential TypeScript 5, Third Edition. The section links point to the parts of the book it draws on.