Typescript Overview

Added Features :

  • Static Typing : you can define types when declaring variables, function parameters, and return values
  • let age: number = 25;
    
    function greet(name: string): void {}
    
    function add(a: number, b: number): number {
        return a + b;
    }
  • Interfaces & Types : allow the use of interface like in Java
  • interface User {
      name: string;
      age: number;
    }
    
    const user: User = { name: "Tuan", age: 999 };
  • Interfaces & Types : allow the use of enumeration, enum
  • enum Color { Red, Green, Blue }
    let c: Color = Color.Green;

Writing Functions

Optional Parameters :

  • Use ? after the parameter to make it optional
  • function greetUser(name: string, age?: number): string {}

Default Parameters :

  • Use = after the type to define default for the parameters
  • function multiply(a: number, b: number = 2): number {}

Function Type Aliases :

  • Use type to define reusable function types
  • type MathOperation = (a: number, b: number) => number;
    
    const add: MathOperation = (a, b) => a + b;
    const subtract: MathOperation = (a, b) => a - b;
    
    console.log(add(5, 3));      //  8
    console.log(subtract(5, 3)); //  2

Interfaces for Function Types :

  • interface can also be used to define reusable function types
  • interface Greet {
      (name: string): string;
    }
    
    const sayHello: Greet = (name) => `Hello, ${name}`;
    
    console.log(sayHello("Bob")); //  "Hello, Bob"

Generics

Allow the use of a placeholder, like <T> or <U> for the actual type

  • Used when you don't know the type in advance
  • Useful for making reusable functions, interfaces, or classes
  • Can be used when working with data structures like arrays, objects, or APIs

Simple Generic :

  • Let Typescript infers the type automatically
  • // Accepts unknown type, with T as an alias, and returns the same type
    function identity<T>(value: T): T {
      return value;
    }
    
    const num = identity<number>(10);   // num: number
    const num = identity(10);    // TS infers T = number

Generic Functions :

  • Make generic, reusable, but still type-safe functions
  • // Merge two objects with unknown types, using T and U as aliases
    function merge<T, U>(a: T, b: U): T & U {
      return { ...a, ...b };
    }
    
    const user = merge({ name: "Bob" }, { age: 22 });
    // user: { name: string; age: number }

Generic Interfaces :

  • Allow interface to hold generic data shapes
  • interface ApiResponse<T> {
      success: boolean;
      data: T;
    }
    
    interface User {
        id: number;
        name: string 
    }
        OR
    type User {
        id: number;
        name: string 
    }
    
    // Defining separate type or interface
    const userResponse: ApiResponse<User[]> = {
      success: true,
      data: [
        { id: 1, name: "Tuan" },
        { id: 2, name: "Bob" },
      ]
    };
    
    // Inlining the object's type
    const productResponse: ApiResponse<string[]> = {
      success: true,
      data: ["Laptop", "Phone", "Tablet"],
    };

Generic Classes :

  • Making generic classes with unknown data type in it
  • class Box<T> {
      content: T;
    
      constructor(value: T) {
        this.content = value;
      }
    
      getContent(): T {
        return this.content;
      }
    }
    
    const numberBox = new Box<number>(123);
    const stringBox = new Box<string>("Hello");
    
    console.log(numberBox.getContent()); // 123
    console.log(stringBox.getContent()); // "Hello"

Generic Constraints :

  • Use extends to only accept types with certain properties
  • Generic constraint: <T extends { length: number }> means the function only allows T to be a type that has a length property of type number
  • // Parameter item can be anything as long as it satisfies the constraint
    function getLength<T extends { length: number }>(item: T): number {
      return item.length;
    }
    
    getLength("Hello"); // ✅ 5
    getLength([1, 2, 3]); // ✅ 3
    getLength(123); // ❌ Error: number doesn't have "length"
    
    ----------------------------------------------
    // Another way to write it
    function getLength(item: { length: number }): number {
      return item.length;
    }

Default Generic Types :

  • Set the default for a type
  • function createArray<T = string>(value: T, count: number): T[] {
      return Array(count).fill(value);
    }
    
    const arr1 = createArray("Hi", 3);   // T = string
    const arr2 = createArray(5, 3);      // T = number
    const arr3 = createArray(undefined, 3); // T = string by default

Converting To Typescript

  1. Install typescript and the type packages for any dependencies you use
    • Next.js and Expo apps will automatically create a tsconfig.json file when run
    • Expo
    // Install typescript and type package for react
    npm install typescript @types/react
    
    // For Web and if using API, Node.js
    npm install @types/react-dom @types/node
    
    // For mobile
    npm install @types/react-native
  2. Add/Update a tsconfig.json file with the sample compiler options below for React project if there isn't one
  3. Common Options

    • target : Decides which JavaScript version TypeScript compiles to.
    • lib : Defines built-in TypeScript libraries. Needed for browser + modern JS features.
    • noEmit : Tells TypeScript not to output JavaScript files (common for React).
    • strict : Enables all strict type checks.
      • Set to false while converting from Javascript to Typescript
    • baseUrl : Set the root of the import.
    • paths : Creates import aliases.
    • resolveJsonModule : Lets you import .json files directly.
    • allowJs : Lets TypeScript compile .js files.
    • include : Tells TS where to look for source files.
    • exclude : Tells TS which folders to ignore.

    Other Options

    • esModuleInterop : Allows default imports for CommonJS modules.
    • allowSyntheticDefaultImports : Similar to above, but JSX-friendly.
    • moduleResolution : How TS finds imports.
    • module : Sets the module system for bundlers.
    • jsx : How JSX is compiled.
    • noImplicitAny : Warns if a variable has an implicit any type.
    • strictNullChecks : Forces you to handle null and undefined explicitly.
    • strictFunctionTypes : Forces you to handle null and undefined explicitly.
    • strictBindCallApply : Ensures functions have correct parameter types..
    {
      "compilerOptions": {
        "target": "ESNext",
        "lib": ["DOM", "DOM.Iterable", "ESNext"],
        "allowJs": true,
        "skipLibCheck": true,
        "esModuleInterop": true,
        "allowSyntheticDefaultImports": true,
        "strict": true,
        "forceConsistentCasingInFileNames": true,
        "module": "ESNext",
        "moduleResolution": "Node",
        "resolveJsonModule": true,
        "isolatedModules": true,
        "noEmit": true,
        "jsx": "react-jsx"
      },
      "include": ["src"],
      "exclude": ["node_modules"]
    }
  4. Rename Files from .js / .jsx to .ts / .tsx
  5. Fix Type Errors
  6. Configure ESLint for TypeScript
    • Install plugins
    • npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin
    • Update the typescript section in eslint config file
    • {
        parser: "@typescript-eslint/parser",
        plugins: ["@typescript-eslint"],
        extends: [
          "eslint:recommended",
          "plugin:react/recommended",
          "plugin:@typescript-eslint/recommended"
        ]
      }
  7. Run and Fix TypeScript Errors
  8. npx tsc --noEmit