Type Inference in TypeScript

Type inference in TypeScript helps compiler to determine (infer) types without explicit declaration.

Type inference allows compiler to infer types automatically when we do not annotate them explicitly. Basically the TypeScript compiler will evaluate the value that will be stored in a variable that doesn’t have type annotation and assign a type to it.

There are multiple ways in which type inference will work.

  • Based on the type of values assigned to variables.
  • Based on the type of arguments passed to function.
  • Based on the return type of function.
  • Contextual typing based on the location of variable.
  • Best Common Type algorithm to determine type for arrays or objects.
// Simple example with variable type inference.

const counter = 10;
// As the value stored in counter is a numerical value, 
// TypeScript compiler will infer it as 'number' type. 

const companyName = 'Elegant TypeScript';
// TypeScript compiler will infer it as 'string' type. 

Sometimes TypeScript might infer type as 'any', it’s because the TypeScript compiler cannot determine the exact type. We will read more about 'any' type in later tutorials.

Variable or Member Typing

Type inference for variables or members can be inferred during the initialization itself. TypeScript compiler will infer the type based on the initialized value.

Let’s take an example of variable initialization to understand better.

// Initializing variables with different value.
let counter = 10;
let name = 'John Doe';
const MONTHS_IN_YEAR = 12;

// checking typeof each variable define.
console.log('Type of counter: ', typeof counter);
console.log('Type of name: ', typeof name);
console.log('Type of MONTHS_IN_YEAR: ', typeof MONTHS_IN_YEAR);

On execution of the above code, we will receive below output. TypeScript will use the same way to determine the type of variable and associate the type to it.

// output 
Type of counter:  number
Type of name:  string
Type of MONTHS_IN_YEAR:  number

Let’s take an object with few member variables and understand how TypeScript will infer the type.

// initializing an object which will contain details of Car
let Car = {
    brand : 'Audi',
    model : 'A6',
    make_year : 2016,
}
Type Inference in TypeScript for Member variables.

The Object 'car' is defined without type definition. When you hover over the ‘Car’ object name on any code editor, the Typing’s will be shown clearly. Even without defining the types TypeScript can understand from the values stored and create an object structure with type.

Best Common type: Union Type

Let’s take an example of array initialization with multiple type of values in the array.

let sampleArray = [1, 'a', '2' , '3'];

TypeScript will use Best Common Type algorithm to analyze each value in the array and then determine what are the type of each value and create a union type, i.e. the array can hold values of either types defined in the union.

In the sampleArray we have defined two types of data, string and number. So TypeScript compiler will add both as union and define as (number | string)[].

// after inference of type it will be similar to below code

let sampleArray (number | string)[] = [1, 'a', '2' , '3'];

Function parameters or arguments Typing

TypeScript can also infer types for function default parameters or arguments automatically.

Let’s take a function that takes two inputs and prints the sum of two.

function addNumber ( a = 10 , b = 20 ) {
	let result = a + b;
  console.log('Result: ' , result);
}

addNumber ( ) ; // output : 30 

TypeScript will check the default value in function arguments, based on which it will infer the type as 'number' for both ‘a’ and ‘b’.

Function return typing

In TypeScript we can define the return type of a function, but if we do not define it TypeScript will infer the type automatically.

We will retake the above function and modify it to return an output and check type inferred by TypeScript.

function addNumber ( a = 10 , b = 20 ) {
	let result = a + b;
	return result;
}

console.log(addNumber ( 30 , 40 ) ) ; // output : 70
// Because the parameters are passed to the function, the default values will be overridden.

console.log(typeof addNumber () ); // output : number

We can see that TypeScript will infer the type as 'number' we can rewrite the function in TypeScript format as shown below where we are annotating type explicitly.

function addNumber ( a : number , b : number ) : number {
  let result = a + b;
  return result;
}
console.log(addNumber ( 30 , 40 ) ) ; // output : 70

Contextual Typing

TypeScript compiler will use the location of variables to infer its type, variable might be part of function parameters or arguments. Let’s take a function which adds event listener.

document.addEventListener('click', function (event) {
    console.log(event.button); // 0
    console.log(typeof event.button) // number
});

TypeScript will identify the 'event' object type using two information i.e. addEventListener function is used and 'click' event is added to the document so it will infer that 'event' is instance of MouseEvent because its a ‘click’ event. So no error is generated when we use event.button

Let’s tweak it a little bit, instead of click event we will change it to scroll event. Now as ‘event’ becomes instance of ScrollEvent we do not have a event.button function so we will get error.

document.addEventListener('scroll', function (event) {
    console.log(event.button);
});
// Here we will get error from TypeScript compiler
// Property 'button' does not exist on type 'Event'.

Contextual typing in TypeScript highlights issues in cases such as arguments to function calls, type assertions, members of objects and array literals, return statements where we might be calling wrong function on the values, as saw in the above example.

Summary

  • Type inference is implicit type assertion for variables, functions, objects etc. which are determined by the TypeScript compiler automatically.
  • TypeScript uses “Best Common Type” algorithm to determine the type implicitly.
  • Type inference is implicit type identification, while Type Annotation is explicit type identification.
  • Contextual typing helps to determine type from imported classes, function or objects to avoid run time errors in application.
Scroll to Top