skip to content
Astro Cactus

Understanding TypeScript Types: A Comprehensive Guide

/ 4 min read

Understanding TypeScript Types

TypeScript’s type system is one of its most powerful features, providing enhanced code quality, better tooling support, and improved developer experience. This comprehensive guide will walk you through everything you need to know about TypeScript types.

Table of Contents

Introduction to Types

TypeScript’s type system adds an extra layer of safety and developer productivity to JavaScript. Types help catch errors early in development, provide better documentation, and enable powerful IDE features like autocomplete and refactoring.

Primitive Types

TypeScript includes all JavaScript primitive types and adds a few additional ones:

// Number
let age: number = 25;
let price: number = 99.99;
// String
let name: string = "John";
let greeting: string = `Hello ${name}`;
// Boolean
let isActive: boolean = true;
// Null and Undefined
let nullValue: null = null;
let undefinedValue: undefined = undefined;
// Symbol
let sym: symbol = Symbol("key");
// BigInt
let bigNumber: bigint = 100n;

Complex Types

Arrays

// Array of numbers
let numbers: number[] = [1, 2, 3];
let numbers2: Array<number> = [1, 2, 3]; // Generic array type
// Array of mixed types
let mixed: (string | number)[] = ["hello", 1, "world"];

Tuples

// Fixed-length array where each element has a specific type
let tuple: [string, number] = ["hello", 42];
// Optional elements
let optionalTuple: [string, number?] = ["hello"];

Objects

// Object type annotation
let user: {
name: string;
age: number;
email?: string; // Optional property
} = {
name: "John",
age: 30
};
// Index signatures
let dictionary: { [key: string]: number } = {
"one": 1,
"two": 2
};

Type Annotations and Inference

TypeScript can often infer types automatically:

// Type inference
let inferredString = "hello"; // Type: string
let inferredNumber = 42; // Type: number
// Explicit type annotations
let explicitString: string = "hello";
let explicitNumber: number = 42;
// Function type annotations
function add(a: number, b: number): number {
return a + b;
}

Literal Types

Literal types allow you to specify exact values:

// String literal types
let direction: "north" | "south" | "east" | "west";
direction = "north"; // Valid
// direction = "northeast"; // Error!
// Numeric literal types
let diceRoll: 1 | 2 | 3 | 4 | 5 | 6;
diceRoll = 1; // Valid
// diceRoll = 7; // Error!
// Boolean literal type
let status: true;
status = true; // Valid
// status = false; // Error!

Union and Intersection Types

Union Types

// Union type
type StringOrNumber = string | number;
let value: StringOrNumber = "hello";
value = 42; // Also valid
// Union with literal types
type Status = "pending" | "approved" | "rejected";
let currentStatus: Status = "pending";

Intersection Types

type HasName = { name: string };
type HasAge = { age: number };
// Intersection type
type Person = HasName & HasAge;
let person: Person = {
name: "John",
age: 30
};

Type Assertions

Type assertions allow you to tell TypeScript that you know better about the type of a value:

// Using angle-bracket syntax
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
// Using 'as' syntax (preferred in JSX)
let someValue2: any = "this is a string";
let strLength2: number = (someValue2 as string).length;
// Non-null assertion
let nullableString: string | null = "hello";
let definiteString: string = nullableString!; // Assert value is non-null

Best Practices

  1. Use Type Inference When Possible
// Good
let message = "Hello"; // Type inference works well here
// Less ideal (unnecessary annotation)
let message: string = "Hello";
  1. Be Specific with Types
// Better
type UserStatus = "active" | "inactive" | "pending";
let status: UserStatus = "active";
// Less ideal
let status: string = "active";
  1. Avoid any Type
// Avoid
function processData(data: any) {
// ...
}
// Better
function processData<T>(data: T) {
// ...
}
  1. Use Type Guards for Runtime Safety
function processValue(value: string | number) {
if (typeof value === "string") {
// TypeScript knows value is a string here
console.log(value.toUpperCase());
} else {
// TypeScript knows value is a number here
console.log(value.toFixed(2));
}
}

Conclusion

Understanding TypeScript types is fundamental to writing better, safer code. They provide compile-time safety, better documentation, and improved developer experience. Remember to:

  • Use types to catch errors early in development
  • Leverage type inference when possible
  • Be specific with your types
  • Use union and intersection types for flexibility
  • Apply type assertions judiciously
  • Follow TypeScript best practices

TypeScript’s type system is extensive and powerful. As you become more comfortable with these basics, you can explore more advanced type features like conditional types, mapped types, and utility types.