TypeScript – How To Avoid “Any”?
26/09/2022
1.08k
- The harmful effects of
any
- Avoiding
any
How to avoid any
?
In the previous blog – Typescript and “any” type, I introduced TypeScript and what exactly is any
type.
In this blog, I’d like to show more about the harmful effects when using any
and introduce some built-in types, features and customs types that you can use to avoid any
.
The harmful effects of any
While TypeScript is a type checker, any
type tells TypeScript to skip/disable type-checking. On the other hand, due to the nature of JavaScript, in some cases providing accurate types isn’t a simple task. In such situations, programmers are tempted to use any
.
In most situations using or implicit any
– a type that allows to store anything and skip type checkers, programmers can’t guarantee what type the value is stored and how it will be used. Furthermore, when the code is executed at runtime, errors may occur even though they were not warned before. For example:
let result; // Variable 'result' implicitly has an 'any' type. result = 10.123; // Number is stored at 'result' console.log(result.toFixed()); // `toFixed()` is a method of `number` result.willExist(); // `willExist()` isn't a method of `number`, but no errors appear.
Because of that, the use of any
is something that should be minimized as much as possible, to ensure the source code does not encounter any errors.
Avoiding any
Based on the basics of TypeScript and Everyday Types, in this blog, I’ll be sharing what I learned and used to write code without any
.
Type aliases & Interfaces
A type alias is exactly a name for any type, you can actually use a type alias to give a name to any type at all, not just an object type. For example:
// Type alias type Point = { x: number, y: number }; type ID = number | string;
An interface declaration is another way to name an object type:
// Interface interface IPoint { x: number, y: number };
Differences between Type Aliases and Interfaces:
// Override type Point = { // TypeError: Duplicate identifier 'Point'. a: string }; interface IPoint { a: string };
Union & Literal types
A union type is a type formed from two or more other types, representing values that may be any one of those types.
// Union types let anyNumber: string | number; // Usage anyNumber = '123'; anyNumber = 123; anyNumber = true; // TypeError: Type 'boolean' is not assignable to type 'string | number'.
In addition to the general types of string and number, you can refer to specific value of strings and numbers.
By combining literals into unions, you can express a much more useful concept. For example:
// Literal types let direction: 'top' | 'left' | 'right' | 'bottom'; direction = 'top'; direction = 'top-right'; // TypeError: Type '"top-right"' is not assignable to type '"top" | "left" | "right" | "bottom"'
Type assertions
Sometimes you will have information about the type of a value that TypeScript can’t know about.
For example, if you’re using document.getElementById
, TypeScript only knows that this will return some kind of HTMLElement
, but you might know that your page will always have an HTMLCanvasElement
with a given ID.
In this situation, you can use a type assertion to specify a more specific type:
// Type assertions const myCanvas = document.getElementById('main-canvas') as HTMLCanvasElement;
Generics
// Example const getRandomNumber = (items: number[]): number => { let randomIndex = Math.floor(Math.random() * items.length); return items[randomIndex]; }; const getRandomString = (items: string[]): string => { let randomIndex = Math.floor(Math.random() * items.length); return items[randomIndex]; }; // Generics function const getRandomGeneric = <T>(items: T[]): T => { let randomIndex = Math.floor(Math.random() * items.length); return items[randomIndex]; }; // Usage const teams: string[] = ['frontend', 'ios', 'android']; const numbers: number[] = [1, 2, 3, 4, 5, 6, 7, 9, 10]; const randomResult1 = getRandomGeneric<string>(teams); const randomResult2 = getRandomGeneric<number>(numbers);
In the example above, the getRandomGeneric
is the generic identity function that worked over a range of types.
The type of generic functions is just like those of non-generic functions, with the type parameters listed first, similarly to function declarations:
const identity = <Type>(param: Type): Type => { return param; };
When calling identity
a function, you now will also need to specify the type of param
that the function will use.
The detail above just Generic identity functions, you can read more generics Generic link
Unknown
unknown
is what should be used when you don’t know a proper type of object. Unlike any
, it doesn’t let you do any operations on a value until you know its type (skip/disable type-checker).
When you unknow something, you need to check before executing. For example:
const invokeAnything = (callback: unknown): void => { if (typeof callback === 'function') { callback(); } if (typeof callback === 'number') { console.log(callback); } if (typeof callback === 'string') { console.log(callback.toUpperCase()); } }; // Usage invokeAnything('typescript'); // Result: TYPESCRIPT
Record for basic object
Probably, nearly every JavaScript developer at some time has used an object as a map-like collection. However, with strict types, it may not be that obvious how to type this. So, you may use interface
, but this way you can’t add anything to the object. Then, you need to think about using Record
.
The definition:
type Record<K extends keyof any, T> = { [P in K]: T; };
And the usage:
// Usage const dict: Record<string, number> = {}; dict.a = 1; dict.b = 'a'; // TypeError: "a" is not assignable to type number let obj: Record<string, number>; obj = { a: 1, b: 2 };
As you can see, it means that the developer can enter any key, but the value has to be of a specific type.
Conclusion
The TypeScript compiler is so powerful. There are so many things we can do with it.
any
type can be avoided with more advanced technics such as interface, type intersection, and the use of generics, etc.
Hope you like it! Enjoy TypeScript and make the code without any
!
Author: Anh Nguyen
Related Blog