Mastering TypeScript: From Beginner to Advanced
A comprehensive guide to TypeScript covering basic types, advanced features, and best practices for modern development.
Mastering TypeScript: From Beginner to Advanced
TypeScript has become an essential tool in modern web development. It adds static typing to JavaScript, making our code more reliable and maintainable. In this comprehensive guide, we’ll explore TypeScript from the basics to advanced concepts.
What is TypeScript?
TypeScript is a superset of JavaScript that adds optional static typing, classes, and modules. It compiles down to plain JavaScript and provides better tooling support through enhanced IntelliSense and error checking.
Basic Types
Primitive Types
let name: string = "John";
let age: number = 30;
let isActive: boolean = true;
let nothing: null = null;
let undefinedValue: undefined = undefined;
let symbolValue: symbol = Symbol("key");
Array Types
let numbers: number[] = [1, 2, 3, 4, 5];
let strings: Array<string> = ["hello", "world"];
let mixed: (string | number)[] = ["hello", 42, "world"];
Object Types
let person: {
name: string;
age: number;
email?: string; // Optional property
} = {
name: "John",
age: 30
};
Interfaces
Interfaces define the structure of objects:
interface User {
id: number;
name: string;
email: string;
age?: number;
readonly createdAt: Date;
}
const user: User = {
id: 1,
name: "John Doe",
email: "john@example.com",
createdAt: new Date()
};
Extending Interfaces
interface Employee extends User {
department: string;
salary: number;
}
const employee: Employee = {
id: 2,
name: "Jane Smith",
email: "jane@example.com",
department: "Engineering",
salary: 75000,
createdAt: new Date()
};
Functions
Function Types
function add(a: number, b: number): number {
return a + b;
}
const multiply: (a: number, b: number) => number = (a, b) => a * b;
Optional and Default Parameters
function greet(name: string, greeting: string = "Hello"): string {
return `${greeting}, ${name}!`;
}
function log(message: string, ...args: any[]): void {
console.log(message, ...args);
}
Generics
Generics allow you to create reusable components that work with multiple types:
function identity<T>(arg: T): T {
return arg;
}
const stringResult = identity<string>("hello");
const numberResult = identity(42); // Type inference
interface Container<T> {
value: T;
getValue(): T;
}
class NumberContainer implements Container<number> {
constructor(public value: number) {}
getValue(): number {
return this.value;
}
}
Advanced Types
Union Types
type Status = "loading" | "success" | "error";
type ID = string | number;
function processStatus(status: Status): void {
switch (status) {
case "loading":
console.log("Loading...");
break;
case "success":
console.log("Success!");
break;
case "error":
console.log("Error occurred");
break;
}
}
Intersection Types
interface HasName {
name: string;
}
interface HasAge {
age: number;
}
type Person = HasName & HasAge;
const person: Person = {
name: "John",
age: 30
};
Conditional Types
type NonNullable<T> = T extends null | undefined ? never : T;
type StringOrNumber<T> = T extends string ? string : number;
type Result1 = NonNullable<string | null | undefined>; // string
type Result2 = StringOrNumber<"hello">; // string
type Result3 = StringOrNumber<42>; // number
Utility Types
TypeScript provides several built-in utility types:
interface User {
id: number;
name: string;
email: string;
age: number;
}
// Partial - makes all properties optional
type PartialUser = Partial<User>;
// Pick - selects specific properties
type UserCredentials = Pick<User, "email" | "name">;
// Omit - excludes specific properties
type UserWithoutId = Omit<User, "id">;
// Record - creates an object type with specific keys and values
type UserRoles = Record<string, "admin" | "user" | "guest">;
Best Practices
1. Use Strict Mode
Enable strict mode in your tsconfig.json
:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}
2. Prefer Interfaces for Object Shapes
// Good
interface User {
name: string;
age: number;
}
// Avoid
type User = {
name: string;
age: number;
};
3. Use Type Guards
function isString(value: unknown): value is string {
return typeof value === "string";
}
function processValue(value: unknown): void {
if (isString(value)) {
// TypeScript knows value is string here
console.log(value.toUpperCase());
}
}
4. Leverage Type Inference
// Let TypeScript infer types when possible
const numbers = [1, 2, 3, 4, 5]; // number[]
const user = { name: "John", age: 30 }; // { name: string; age: number }
// Only add explicit types when necessary
function process<T>(items: T[]): T[] {
return items.reverse();
}
Real-World Example
Here’s a practical example combining multiple concepts:
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
interface User {
id: number;
name: string;
email: string;
}
class UserService {
private baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
async getUser(id: number): Promise<ApiResponse<User>> {
try {
const response = await fetch(`${this.baseUrl}/users/${id}`);
const data = await response.json();
return {
data,
status: response.status,
message: "User retrieved successfully"
};
} catch (error) {
throw new Error(`Failed to fetch user: ${error}`);
}
}
async createUser(userData: Omit<User, "id">): Promise<ApiResponse<User>> {
try {
const response = await fetch(`${this.baseUrl}/users`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(userData)
});
const data = await response.json();
return {
data,
status: response.status,
message: "User created successfully"
};
} catch (error) {
throw new Error(`Failed to create user: ${error}`);
}
}
}
// Usage
const userService = new UserService("https://api.example.com");
userService.getUser(1).then(response => {
console.log(response.data.name);
});
userService.createUser({
name: "Jane Doe",
email: "jane@example.com"
}).then(response => {
console.log(`Created user with ID: ${response.data.id}`);
});
Conclusion
TypeScript is a powerful tool that can significantly improve your development experience and code quality. By understanding these concepts and following best practices, you’ll be able to write more maintainable and reliable code.
Remember that TypeScript is designed to help you, not hinder you. Start with the basics and gradually incorporate more advanced features as you become comfortable with them.
Happy coding! 🚀