typescript verse...

Abhay AgnihotriAbhay Agnihotri
6 min read

Let’s Go from Basics to Advance🚀

Why you Should Read ?

Whether you’re a coding novice, a seasoned pro, or just curious about typescript, this guide will make typescript’s core concepts a breeze. So get ready to dive into the world of typescript!

JavaScript vs TypeScript

  1. Typing:

    • JavaScript: Dynamic typing (types checked at runtime).

    • TypeScript: Static typing (types checked at compile time).

  2. Compilation:

    • JavaScript: Runs directly in the browser.

    • TypeScript: Compiles to JavaScript before running, catching errors early.

  3. Tooling:

    • JavaScript: Basic autocompletion.

    • TypeScript: Enhanced IDE support with autocompletion and type-checking.

To run any typescript code, open your terminal and type tsc for general compilation or tsc app.ts for a specific file. For real-time compilation as you type, go with tsc --watch and let typescript do the rest!

🎉 Tuples: Fixed-Structure Arrays

  • Tuples are like arrays, but they have a fixed type and length, making data structure predictable.

  • Each element in a tuple has a specific, enforced type, keeping things organized.

  • Great for scenarios where each item has a different role or meaning.

// Fixed length and types
let personInfo: [number, string] = [22, 'Abhay'];
// Throws an error if altered, keeping data structure predictable.

Think of tuples as strict shopping lists where every item has a specific type and place!

📜 Enums: Structured Sets of Constants

  • typescript enum organize related constants under one name, like user roles or status codes.

  • They look like objects but add meaningful labels, making code easier to read.

  • Access values with labels, so no need to remember raw strings or numbers!

enum UserRoles {
    GUEST = 'GUEST',
    ADMIN = 'ADMIN',
    SUPER_ADMIN = 'SUPER_ADMIN'
}
// Now you can use these roles with clear labels

Enums prevent you from having to remember random codes or values, letting you access them with readable names like UserRoles.GUEST.

⚙️ Types: Any, Unknown, Void, and More

TypeScript offers a variety of types to handle different data in predictable ways. Here are a few you’ll encounter often:

  • Any: The universal type, accepting any value and allowing reassignment, which can sometimes lead to unpredictability.
let anything: any = 10; // Can be reassigned to any type
anything = "Now I’m a string!"; // Valid because of 'any' type
  • Unknown: Like any but more restrictive; you need to confirm the type before using it.
let uncertainValue: unknown;
uncertainValue = "Hello, TypeScript!";
if (typeof uncertainValue === "string") {
    console.log(uncertainValue.toUpperCase()); // Safe to access as a string
}

Using unknown ensures you verify types before interacting with the data, reducing unexpected errors.

🧑‍💻 Interfaces: Defining Object Structures

  • typescript interface acts as a blueprint for objects, defining which properties are required.

  • Interfaces provide a clear structure, so TypeScript can check if objects meet the expected shape.

  • Great for defining object structures for consistent data handling.

interface User {
    name: string;
    email: string;
    password: string;
}

// Function accepts only User-type objects
function greetUser(user: User) {
    console.log(`Hello, ${user.name}`);
}

Interfaces set a clear structure for objects, so TypeScript will alert you if anything’s missing or incorrect.

🔄 Extending Interfaces: Adding Properties

  • Interface extensions allow one interface to inherit from another, creating more complex structures.

  • Instead of rewriting code, extensions add properties to existing structures.

  • Perfect for hierarchical data structures like User and Admin.

interface User {
    name: string;
    email: string;
}

interface Admin extends User {
    isAdmin: boolean;
}

By extending User to Admin, TypeScript knows an Admin has both name and email plus the new isAdmin property.

🏛 Classes and Constructors: Object Templates

  • Classes organize data and methods into reusable templates, perfect for defining object blueprints.

  • Constructors set initial values when a new instance is created.

  • Ideal for creating multiple objects with the same properties and methods.

class User {
    constructor(public name: string, public email: string, private password: string) {}
}

const newUser = new User("Abhay", "abhay@example.com", "password123");

With classes, you can create multiple instances, each with its own properties, without writing repetitive code.

🧩 Public, Private, and Protected: Access Control

Access modifiers let you control who can interact with certain parts of your code, making it organized and secure:

  1. Public: Accessible everywhere (default).

  2. Private: Accessible only within the class.

  3. Protected: Accessible within the class and subclasses.

class Car {
    public model: string;
    private engine: string;

    constructor(model: string, engine: string) {
        this.model = model;
        this.engine = engine;
    }

    private startEngine() {
        console.log("Engine started");
    }
}

Using access modifiers keeps your data safe from accidental changes.

🧰 Getters, Setters, and Static Members

  • Getters and setters control access and modification of properties, adding an extra layer of data management.

  • Static members belong to the class itself, not to specific instances.

  • Ideal for utility functions or data that should be consistent across all instances.

class MathUtils {
    static pi: number = 3.14159;

    static calculateArea(radius: number): number {
        return this.pi * radius * radius;
    }
}

console.log(MathUtils.calculateArea(5)); // Output: 78.53975

Static methods act like shared tools, available to all instances of the class without needing instantiation.

🎩 Abstract Classes and Methods: Skeleton Templates

  • Abstract classes provide a template but leave specifics to subclasses, offering a structured but flexible setup.

  • Abstract methods must be defined in subclasses, enforcing consistent behavior.

  • Perfect for creating a general structure that subclasses build upon.

abstract class Animal {
    constructor(public name: string) {}

    abstract makeSound(): void; // Subclasses must define this

    move(): void {
        console.log(`${this.name} is moving`);
    }
}

class Dog extends Animal {
    makeSound(): void {
        console.log("Woof! Woof!");
    }
}

By using abstract, you set a standard while giving freedom to subclasses for specific implementations.

🔀 Generics: Flexible, Reusable Code

  • typescript generics allow you to create flexible code that adapts to multiple types while maintaining type safety.

  • Perfect for utility functions or classes that can work with various data types.

  • You define the specific type when using the generic function or class.

function identity<T>(value: T): T {
    return value;
}

console.log(identity<string>("Hello")); // Output: Hello
console.log(identity<number>(42));      // Output: 42

Generics ensure type safety while giving flexibility, making code more adaptable to different use cases.

🔎 Type Guards: Ensuring Type Safety

  • Type guards confirm the type of a variable, so TypeScript can make informed decisions about how to handle it.

  • Type assertions like typeof and instanceof help narrow down types within conditionals.

  • Useful for managing data with mixed types or unknown values.

function isString(value: unknown): value is string {
    return typeof value === "string";
}

let value: unknown = "Hello";
if (isString(value)) {
    console.log(value.toUpperCase()); // Safely treats 'value' as a string
}

Type guards are TypeScript’s way of confirming data types, helping avoid errors when working with unknown or mixed types.

Thanks for Reading ✌️😎

Happy Coding !

0
Subscribe to my newsletter

Read articles from Abhay Agnihotri directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Abhay Agnihotri
Abhay Agnihotri