any
Typenull
and undefined
any
TypeIn TypeScript, if you don’t explicitly specify a type for a variable and TypeScript cannot infer it, the variable will be implicitly assigned the any
type. Using any
defeats the purpose of TypeScript as it bypasses type checking, potentially leading to runtime errors.
// Implicit any
let myVariable;
myVariable = "Hello";
myVariable = 123; // No type error, but it might lead to unexpected behavior
function add(a, b) {
return a + b;
}
const result = add("1", 2); // This will concatenate instead of adding numbers
noImplicitAny
in tsconfig.json
: This forces you to explicitly specify types for variables.{
"compilerOptions": {
"noImplicitAny": true
}
}
let myVariable: string | number;
myVariable = "Hello";
myVariable = 123;
function add(a: number, b: number): number {
return a + b;
}
const result = add(1, 2);
Type assertions are used to tell the TypeScript compiler that you know more about a value’s type than it does. However, misusing type assertions can lead to type - safety issues. If you assert a value to a type that it doesn’t actually belong to, you can introduce bugs.
const value: any = "Hello";
const numValue = value as number;
// This will compile, but at runtime, numValue will still be a string
console.log(numValue.toFixed(2)); // This will throw a runtime error
function isNumber(value: any): value is number {
return typeof value === 'number';
}
const value: any = "Hello";
if (isNumber(value)) {
const numValue = value;
console.log(numValue.toFixed(2));
} else {
console.log("Value is not a number");
}
const element = document.getElementById('myDiv') as HTMLDivElement;
// Only use this if you are sure that the element with id 'myDiv' is a div
Union types allow a variable to have one of several types. While they are useful, overusing them can make the code hard to understand and maintain. Complex union types can also lead to more conditional logic in the code.
function printValue(value: string | number | boolean | null | undefined) {
if (typeof value === 'string') {
console.log(`It's a string: ${value}`);
} else if (typeof value === 'number') {
console.log(`It's a number: ${value}`);
} else if (typeof value === 'boolean') {
console.log(`It's a boolean: ${value}`);
} else if (value === null) {
console.log('It\'s null');
} else if (value === undefined) {
console.log('It\'s undefined');
}
}
type StringOrNumber = string | number;
type BooleanOrNull = boolean | null;
type ValueType = StringOrNumber | BooleanOrNull | undefined;
function printValue(value: ValueType) {
if (typeof value === 'string' || typeof value === 'number') {
console.log(`It's a string or number: ${value}`);
} else if (typeof value === 'boolean' || value === null) {
console.log(`It's a boolean or null: ${value}`);
} else if (value === undefined) {
console.log('It\'s undefined');
}
}
null
and undefined
In TypeScript, null
and undefined
are separate types. Ignoring their presence can lead to runtime errors when trying to access properties or call methods on null
or undefined
values.
function getLength(str: string) {
return str.length;
}
const nullableStr: string | null = null;
const length = getLength(nullableStr); // This will throw a runtime error
strictNullChecks
in tsconfig.json
: This makes TypeScript more strict about null
and undefined
values.{
"compilerOptions": {
"strictNullChecks": true
}
}
function getLength(str: string | null | undefined) {
return str?.length;
}
const nullableStr: string | null = null;
const length = getLength(nullableStr); // Returns undefined instead of throwing an error
const defaultStr = nullableStr ?? "Default";
Circular dependencies occur when two or more modules depend on each other directly or indirectly. In TypeScript, circular dependencies can lead to issues such as incomplete object initialization and hard - to - debug errors.
moduleA.ts
import { ClassB } from './moduleB';
export class ClassA {
constructor() {
const b = new ClassB();
}
}
moduleB.ts
import { ClassA } from './moduleA';
export class ClassB {
constructor() {
const a = new ClassA();
}
}
// common.ts
export function commonFunction() {
// Common functionality
}
// moduleA.ts
import { commonFunction } from './common';
export class ClassA {
constructor() {
commonFunction();
}
}
// moduleB.ts
import { commonFunction } from './common';
export class ClassB {
constructor() {
commonFunction();
}
}
TypeScript is a powerful language that can significantly improve the quality of your JavaScript code. However, being aware of these common pitfalls and knowing how to avoid them is crucial for writing robust and maintainable TypeScript applications. By following the best practices outlined in this blog post, you can minimize the chances of encountering bugs and make your development process smoother.