← Back to home

Typescript: Type inference and explicit typing

Cover Image for Typescript: Type inference and explicit typing
5 min readLevel:

As you should be aware, TypeScript adds optional static typing to JavaScript. This allows us to use types. For example, in this code:

main.ts
let name = 'Eric'

The moment we assign a value to a variable, TypeScript tries to infer the type from the initialization. In that case, the type of name variable is inferred to be string.

Error: Type 'number' is not assignable to type 'string'

As can be seen, TypeScript warns us that we are mixing types. TypeScript uses a concept called type inference to warn about unwanted errors.

Type inference is the automatic deduction of the data types of specific expressions in a programming language.

Otherwise, in JavaScript, you can assign different values and values of different types. JavaScript doesn't care about the type.

main.js
let name = 'Eric';
name = 123;

In most cases, type inference is straightforward. How can the types be inferred in complex cases? There are two special situations that need more detail: Best common type and Contextual typing.

Best common type

'Best common type' is calculated when the type is inferred using several expressions. The type of those expressions is used to infer the type of the variable. For example, in this code:

main.typescript
let x = [0, 1, false, true, "false", "true"];

TypeScript considers the type of each array element to infer the type of x. In that case, the type of each array element can be number, boolean or string.

The best common type algorithm considers each candidate type and picks the type that is compatible with all the possibilities.

Type inferred in array

In some cases, the 'best common type' approach is not enough strict or specific so sometimes you may like to do something more strict. To do so, you will need to type explicitly a strict typing (BooleanValuesStrict):

Boolean values type strict declaration

With that, you are being more explicit and strict with your typing.

In some cases, we cannot rely on the inference and we need to do explicit typing to provide more robustness to our code.

As can be seen, if we use explicit typing, we are ensuring that only specific values are allowed and TypeScript throws an error at us.

Explicit typing is useful when TypeScript doesn't have enough context to infer the type. Even so, using it everywhere can make some of them redundant, resulting in a verbose codebase.

Adding explicit type annotation to all the variables and functions is redundant and doesn't have any kind of benefit.

main.ts
const gretting = (msg: string, name: string): string => {
    const grettings: string = `${msg} ${name}`;
    return grettings;
}

const message: string = 'Hello';
const grettings: string = gretting(message, 'visitor');

console.log(grettings)

The above code is repetitive and we can achieve the same results when types are inferred. This code is more readable and preferable:

main.ts
const gretting = (msg: string, name: string) => {
    const grettings = `${msg} ${name}`;
    return grettings;
}

const message = 'Hello';
const grettings = gretting(message, 'visitor');

console.log(grettings)

There are cases where the best common type can be summarized using the super type of all the candidates. For example, we can define 3 types: Course, OnlineCourse and PresentialCourse.

main.ts
interface Course {
    id: string;
    name: string;
    description: string;
    date: string;
    places: number;
}

interface OnlineCourse extends Course {
    link: string;
}

interface PresentialCourse extends Course {
    address: string;
    room: string;
}

const reactCourse: OnlineCourse = {
    id: '123',
    name: 'React Course 2022',
    description: 'Intro to React',
    date: "Jul 15 2022",
    places: 50,
    link: 'www.ealch.dev/react-course',
};

const angularCourse: PresentialCourse = {
    id: '124',
    name: 'Angular Course 2022',
    description: 'Intro to Angular',
    date: "Jul 15 2022",
    places: 15,
    address: '123 Street',
    room: '1-2'
}

TypeScript is able to infer the type of course if we do left-side hand typing in the array elements:

Array of OnlineCourse and PresentialCourse

When no 'best common type' is found, the resulting inference is the union array type. Anyway, we are able to do that:

Array of OnlineCourse and PresentialCourse with left-side hand typing

Contextual typing

Contextual typing occurs when the type of an expression is implied by its location. For example, in this code:

The type checker has used the type of the Window.onclick function to infer the type of the function expression on the right-hand side of the assignment. The type checker was able to infer the type of event parameter, which does not contain name property.

We can try typing the parameter as NamedMouseEvent:

But this will not work since this breaks the Window.onclick typing:

To solve this, we have, at least, two different options:

Contextual typing applies in many cases. In fact, the contextual type can act as a candidate type in the 'best common type'.

You can find more information in the Type inference section.

If you find any kind of error, something that it's not clear enough or you want me to drill down in more detail, don't hesitate to contact me.