If you are a developer who writes code in TypeScript, you probably have come across the “any” type. You often use “any” type when working with dynamic values or those whose types are unknown. It helps you bypass the strict rules TypeScript imposes on variable types. However, this approach carries certain risks. Evading TypeScript’s safety checks results in errors and bugs in your code.
This blog post will showcase why you mustn’t use “any” type to write code. It will also reveal how to use TypeScript’s built-in features to write impeccable codes. Let’s dive into it.
The any type in TypeScript is a special type that can hold values of any kind. For example, a variable of type any can store a number, a string, an object, or a function. TypeScript will not apply standard rules to such a variable. It lets you perform any operation on it. Also, TypeScript won’t point out any errors, if you add a number and a string or call a method on an object that doesn’t exist. You can read the official TypeScript documentation for more details.
There are instances where the use of “any” type is suitable. For example, when dealing with external libraries or APIs that may not have type definitions. In this case, “any” type helps you compile code without fussing about the type information.
Alternatively, when you move a project from JavaScript to TypeScript, you may not be prepared to assign types to everything promptly. In this case, you can evade the standard TypeScript checks and make your code functional by employing “any” type. However, these actions carry a trade-off. TypeScript, with its safety checks, helps detect errors in the early stages of code development. It also helps write highly readable codes. Without TypeScript standard checks, your code will likely contain errors and lack clarity.
Using ‘any’ in TypeScript can be likened to telling the compiler, “I’ve got this—trust me!” But, this is a gamble that can backfire and cause many bugs and issues in your code. By opting for ‘any,’ you essentially forget the benefits of TypeScript’s early error detection, transforming your code into what is essentially JavaScript, devoid of static typing advantages.
Embracing the ‘any’ type liberally might seem convenient, but it comes at a cost. Here’s why it’s considered bad practice:
Lost Type Safety: TypeScript shines in catching type-related errors early in development. ‘any’ diminishes this advantage, leaving your code susceptible to bugs that might slip through unnoticed.
// Function with `any` type
function processData(data: any[]): any[] {
// Here, we can get a runtime error when the item has no
// property named value, it is a possibility since we have
// `data` with `any` type
return data.map(item => ({ processedValue: processString(item.value) }));
}
// Function without `any` type
interface Item {
value: string;
}
function processData(data: Item[]): { processedValue: string }[] {
// No chance of running into runtime error as we have assigned
// definitive types to parameters now compiler knows that item
// will definitely have property value
return data.map(item => ({ processedValue: processString(item.value) }));
}
Reading Challenges: Overreliance on ‘any’ can make code less readable. Instead of clear and specific types, your code becomes a puzzle developers must decipher, hindering collaboration and maintenance.
// Function using any type
function concatenate(x: any, y: any): any {
// This function could potentially be used to add any type
// of variable which might lead to runtime errors, developer
// new to the codebase might not know what this function does
return x + y;
}
// Function without using any type
function concatenate(x: string, x: string): string {
// This function definition itself documents it and provides
// better readability
return x + y;
}
Now that we understand the perils, let’s explore practical strategies to liberate our code from the clutches of ‘any.’
Embrace Specific Types: Instead of resorting to the generic ‘any,’ opt for more specific types like number, string, boolean, or custom types such as classes and interfaces. This not only enhances clarity but also fortifies your code.
let num: any;
num = 10.123;
console.log(num.toFixed());
num.willExist(); // won't show any error as num has `any` type
let num: number;
num = 10.123;
console.log(num.toFixed());
num.willExist(); // will show an error now
Leverage Type Inference: Let TypeScript work its magic by allowing type inference. Often, TypeScript can deduce types from assigned values, reducing the need for explicit type declarations.
let result = 3; // result implicitly has number type
result = "three" // This will throw an error.
Harness Unions and Intersections: Employ type unions (using |) and intersections (using &) to create complex types. This lets you express broader possibilities without resorting to the catch-all ‘any.’
// Union Example
// `myVariable` will have either a string or null value,
// we cannot assign anything else
let myVariable: string | null;
// Intersection Example
type Person = {
name: string;
age: number;
};
type Worker = {
jobTitle: string;
salary: number;
};
type Employee = Person & Worker;
Embrace ‘unknown’: When uncertain about a variable’s type, consider using ‘unknown.’ It provides flexibility like ‘any,’ but with the caveat that you must ascertain the type before usage, offering a safety net against runtime errors.
// any type
let x: any = 'any type variable';
console.log('Value of x : ' + x);
let y: string = x;
console.log('Value of y : ' + y);
// unknown type
let x: unknown = 'unknown type variable';
console.log('Value of x : ' + x);
let y: string = x;
console.log('Value of y : ' + y);
Define Object Types: If primitive types don’t suffice, define object types using interfaces, types, or generics. This provides a clear roadmap for TypeScript, enhancing its understanding of acceptable values.
// Using 'type' to define a type
type PersonType = {
name: string;
age: number;
greet: (greeting: string) => void;
};
// Using 'interface' to define a type
interface PersonInterface {
name: string;
age: number;
greet: (greeting: string) => void;
}
The risks associated with the utilization of the “any” type in TypeScript have been detailed with clarity in this blog post. The errors and bugs that arise due to the use of the ‘any’ type can go undetected by TypeScript.
However, there’s a feasible alternative. Robustly framed coding can be done by using TypeScript’s built-in features and tools. These include using specific types, leveraging type inference, harnessing unions and intersections, embracing ‘unknown’, and defining object types. This way, you can create powerful and expressive code that comes under the safety net of TypeScript.
Our intent with this blog was to help introduce something new and useful. TypeScript is a powerful and innovative language that helps you write better code. If you undertake a correct and disciplined approach, you can leverage it to your best advantage.
Do you want to create effective web and mobile apps? With Aubergine Solutions, you have to look no further since we have got you covered. We have expertise in TypeScript and many other web and mobile technologies. Our team consists of expert software developers who not only assist you with your project but cater to all your project needs, right from the beginning phase of design to development. We can help you design and develop diverse kinds of websites, including simple, basic framework-based websites, to an intricate web app, or a cross-platform mobile app. Contact us today.