TypeScript and “any” type

0

0

07/09/2022

Table of contents

  • What is TypeScript?
  • Basic typing
  • What is TypeScript any type?
  • Why does TypeScript provide any?

TypeScript is a strongly typed programming language that builds on JavaScript, giving you a better ability to detect errors and describe your code. But sometimes you don’t know the exact type of value that you’re using because it comes from user input or a third-party API.

In this case, you want to skip the type checking and allow the value to pass through the compile check. The TypeScript any type is the perfect solution for you because if you use it, the TypeScript compiler will not complain about the type issue.

This blog will help you understand the any type in TypeScript but before doing that, let’s begin with some basic concepts first!

What is TypeScript?

TypeScript checks a program for errors before execution, and does so based on the kinds of values, it’s a static type checker.

Superset of JavaScript

TypeScript is a language that is a superset of JavaScript: JS syntax is therefore legal TS. However, TypeScript is a typed superset, meaning that it adds rules about how different kinds of values can be used.

Runtime Behavior

TypeScript is also a programming language that preserves the runtime behavior of JavaScript. This means that if you move code from JavaScript to TypeScript, it is guaranteed to run the same way, even if TypeScript thinks that the code has type errors.

Erased Types

Roughly speaking, once TypeScript’s compiler is done with checking your code, it erases the types to produce the resulting compiled code. This means that once your code is compiled, the resulting plain JS code has no type information.

An easy way of understanding TypeScript

  • A language
  • A superset of JavaScript
  • Preserver the runtime behavior of JavaScript
  • Type checker layer

JavaScript + Types = TypeScript

Basic typing

Type annotations

TypeScript uses type annotations to explicitly specify types for identifiers such as variables, functions, objects, etc.

// Syntax
: type

Once an identifier is annotated with a type, it can be used as that type only. If the identifier is used as a different type, the TypeScript compiler will issue an error.

let counter: number;
counter = 1;
counter = 'Hello'; // Error: Type '"Hello"' is not assignable to type 'number'.

The following shows other examples of type annotations:

let name: string = 'John';
let age: number = 25;
let active: boolean = true;

// Array
let names: string[] = ['John', 'Jane', 'Peter', 'David', 'Mary'];

// Object
let person: {
  name: string;
  age: number
};
person = {
  name: 'John',
  age: 25
}; // Valid

// Function
let sayHello : (name: string) => string;
sayHello = (name: string) => {
  return `Hello ${name}`;
};

Type inference

Type inference describes where and how TypeScript infers types when you don’t explicitly annotate them. For example:

// Annotations
let counter: number;

// Inference: TypeScript will infer the type the `counter` to be `number`
let counter = 1;

Likewise, when you assign a function parameter a value, TypeScript infers the type of the parameter to the type of the default value. For example:

// TypeScript infers type of the `max` parameter to be `number`
const setCounter = (max = 100) => {
  // ...
}

Similarly, TypeScript infers the return type to the type of the return value:

const increment = (counter: number) => {
  return counter++;
}

// It is the same as:
const increment = (counter: number) : number => {
  return counter++;
}

The following shows other examples of type inference:

const items = [0, 1, null, 'Hi']; // (number | string)[]
const mixArr = [new Date(), new RegExp('\d+')]; // (RegExp | Date)[]
const increase = (counter: number, max = 100) => {
  return counter++;
}; // (counter: number, max?: number) => number

Contextual typing

TypeScript uses the locations of variables to infer their types. This mechanism is known as contextual typing. For example:

document.addEventListener('click', (event) => {
  console.log(event.button); // Valid
});

In this example, TypeScript knows that the event the parameter is an instance of MouseEvent because of the click event.

However, when you change the click event to the scroll the event, TypeScript will issue an error:

document.addEventListener('scroll', (event) => {
  console.log(event.button); // Compile error
}); // Property 'button' does not exist on type 'Event'.

TypeScript knows that the event in this case, is an instance of UIEvent, not a MouseEvent. And UIEvent does not have the button property, therefore, TypeScript throws an error.

Other examples about contextual typing

// Array members
const names = ['John', 'Jane', 'Peter', 'David', 'Mary']; // string[]
names.map(name => name.toUpperCase()); // (name: string) => string

// Type assertions
const myCanvas = document.getElementById('main-canvas') as HTMLCanvasElement;

Type inference vs Type annotations

Type inferenceType annotations
TypeScript guesses the typeYou explicitly tell TypeScript the type

What exactly is TypeScript any?

When you don’t explicitly annotate and TypeScript can’t infer exactly the type, that means you declare a variable without specifying a type, TypeScript assumes that you use the any type. This practice is called implicit typing. For example:

let result; // Variable 'result' implicitly has an 'any' type.

So, what exactly is any?

TypeScript any is a special type that you can use whenever you don’t want a particular value to cause type-checking errors. That means, the TypeScript compiler doesn’t complain or issue any error.

When a value is of type any, you can access any properties of it, call it like a function, assign it to (or from) a value of any type, or pretty much anything else that’s syntactically legal:

let obj: any = { x: 0 };
// None of the following lines of code will throw compiler errors.
// Using `any` disables all further type checking, and it is assumed 
// you know the environment better than TypeScript.
obj.foo();
obj();
obj.bar = 100;
obj = 'hello';
const n: number = obj;

Looking back at an easier to understand any:

  • A special type
  • Skip/Disable type-checking
  • TypeScript doesn’t complain any or issue any error
  • Default implicit typing is any.

Note that to disable implicit typing to the any type, you change the noImplicitAny option in the tsconfig.json file to true. Read more noImplicitAny

Why does TypeScript provide any type?

As described above, while TypeScript is a type checker, any type tell TypeScript to skip/disable type-checking.

Whether TypeScript has made a mistake here and why it provides any type?

In fact, sometimes the developer can’t determine the type of value or can’t determine the return value from the 3rd party. Most of cases they use any the type or use implicit typing as any. So they seem to think that TypeScript provides any to do those things.

So, is that the root reason that TypeScript provides any?

Actually, I think there is a more compelling reason for TypeScript providing any that the any type provides you with a way to work with the existing JavaScript codebase. It allows you to gradually opt-in and opt out of type checking during compilation. Therefore, you can use the any type for migrating a JavaScript project over to TypeScript.

Conclusion

TypeScript is a Type checker layer.

The TypeScript any type allows you to store a value of any type. It instructs the compiler to skip type-checking.

Use the any type to store a value when you migrate a JavaScript project over to a TypeScript project.

In the next blog, I will show you more about the harmful effects of any and how to avoid them.

Hope you like it! See you in the next blog!

Reference

Author: Anh Nguyen