Skip to content
TypeScript Programming Language Guide — Typed JavaScript for Scalable Apps

TypeScript Programming Language Guide — Typed JavaScript for Scalable Apps

DodaTech Updated Jun 7, 2026 7 min read

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript, adding static type checking for catching errors at compile time rather than runtime.

What You’ll Learn

  • Types, interfaces, and type annotations
  • Generics for reusable, type-safe code
  • Enums and type narrowing
  • Utility types for common transformations
  • tsconfig and compiling to JavaScript

Why It Matters

TypeScript catches bugs before they reach production. Doda Browser’s frontend uses TypeScript to prevent the “undefined is not a function” errors that plague large JavaScript codebases. TypeScript provides documentation through types — any developer can understand a function’s contract by reading its signature. Major frameworks like Angular, Next.js, and Vue all recommend TypeScript. It’s the standard for professional JavaScript development at scale.

Learning Path

    flowchart LR
  A[TypeScript Basics<br/>You are here] --> B[Types & Interfaces]
  B --> C[Generics & Enums]
  C --> D[Type Narrowing]
  D --> E[Build a Typed API Client]
  

Setting Up TypeScript

Install TypeScript via npm and compile to JavaScript.

npm install -g typescript
// greet.ts
function greet(name: string): string {
    return `Hello, ${name}!`;
}

console.log(greet("TypeScript"));  // Hello, TypeScript!
tsc greet.ts
node greet.js
# Hello, TypeScript!

Basic Types and Annotations

TypeScript extends JavaScript with static types.

// Primitive types
let name: string = "Alice";
let age: number = 30;
let isActive: boolean = true;
let data: any = "can be anything";  // avoid when possible

// Arrays
let numbers: number[] = [1, 2, 3];
let strings: Array<string> = ["a", "b", "c"];

// Tuples
let pair: [string, number] = ["Alice", 30];

// Union types
let id: string | number = "abc123";
id = 456;  // OK

// Type inference — TypeScript infers types automatically
let message = "Hello";  // inferred as string

Interfaces

Interfaces define the shape of objects.

interface User {
    id: number;
    name: string;
    email: string;
    age?: number;  // optional property
    readonly createdAt: Date;  // cannot be changed after initialization
}

function createUser(name: string, email: string): User {
    return {
        id: Math.floor(Math.random() * 1000),
        name,
        email,
        createdAt: new Date()
    };
}

function displayUser(user: User): void {
    console.log(`${user.name} (${user.email})`);
    if (user.age) {
        console.log(`Age: ${user.age}`);
    }
}

const alice = createUser("Alice", "alice@example.com");
displayUser(alice);
// Alice (alice@example.com)

Generics

Generics create components that work with any type while preserving type safety.

// Generic function
function firstElement<T>(arr: T[]): T | undefined {
    return arr[0];
}

console.log(firstElement([1, 2, 3]));         // 1 (type: number)
console.log(firstElement(["a", "b"]));        // a (type: string)

// Generic interface
interface Repository<T> {
    getById(id: number): T | undefined;
    getAll(): T[];
    create(item: T): void;
    delete(id: number): void;
}

class UserRepository implements Repository<User> {
    private users: User[] = [];

    getById(id: number): User | undefined {
        return this.users.find(u => u.id === id);
    }

    getAll(): User[] {
        return [...this.users];
    }

    create(user: User): void {
        this.users.push(user);
    }

    delete(id: number): void {
        this.users = this.users.filter(u => u.id !== id);
    }
}

Enums and Type Narrowing

Enums define named constants. Type narrowing refines types within conditional blocks.

enum Direction {
    North = "NORTH",
    South = "SOUTH",
    East = "EAST",
    West = "WEST"
}

function move(direction: Direction, distance: number): void {
    console.log(`Moving ${direction} by ${distance} units`);
}

move(Direction.North, 10);
// Moving NORTH by 10 units

// Type narrowing with discriminated unions
interface Circle {
    kind: "circle";
    radius: number;
}

interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}

type Shape = Circle | Rectangle;

function area(shape: Shape): number {
    switch (shape.kind) {
        case "circle":
            return Math.PI * shape.radius ** 2;
        case "rectangle":
            return shape.width * shape.height;
    }
}

console.log(area({ kind: "circle", radius: 5 }));     // 78.5398...
console.log(area({ kind: "rectangle", width: 4, height: 6 })); // 24

Utility Types

TypeScript provides built-in utility types for common type transformations.

interface Todo {
    title: string;
    description: string;
    completed: boolean;
    createdAt: Date;
}

// Partial — all properties optional
function updateTodo(todo: Todo, updates: Partial<Todo>): Todo {
    return { ...todo, ...updates };
}

const todo: Todo = {
    title: "Learn TypeScript",
    description: "Study types and interfaces",
    completed: false,
    createdAt: new Date()
};

const updated = updateTodo(todo, { completed: true });

// Pick — select specific properties
type TodoPreview = Pick<Todo, "title" | "completed">;

// Omit — exclude specific properties
type TodoWithoutDates = Omit<Todo, "createdAt">;

// Readonly — all properties become readonly
type ImmutableTodo = Readonly<Todo>;

// Record — key-value map
type PageInfo = Record<string, { title: string; url: string }>;

const pages: PageInfo = {
    home: { title: "Home", url: "/" },
    about: { title: "About", url: "/about" }
};

tsconfig and Compilation

The tsconfig.json file controls TypeScript compiler options.

{
    "compilerOptions": {
        "target": "ES2020",
        "module": "commonjs",
        "strict": true,
        "outDir": "./dist",
        "rootDir": "./src",
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules", "dist"]
}
tsc              # compiles all files matching include patterns
tsc --watch      # watch mode — recompiles on changes

Common Mistakes

1. Using any instead of proper types

any disables type checking. Use unknown when you truly don’t know the type, then narrow it.

2. Not enabling strict mode

// Without strict mode, TypeScript allows implicit any
function log(obj) {  // parameter is implicitly any
    console.log(obj);
}

Set "strict": true in tsconfig.

3. Confusing interface with type

Interfaces can be extended and merged; type aliases can represent unions and primitives. Use interfaces for object shapes, types for everything else.

4. Forgetting to compile before running

TypeScript must be compiled to JavaScript. Use tsc or ts-node for development.

5. Incorrect null checking

function process(value: string | null) {
    value.toUpperCase();  // Error: Object is possibly null
    value!.toUpperCase(); // Non-null assertion (risky)
    value?.toUpperCase(); // Safe — returns undefined if null
}

6. Overusing as type assertions

Type assertions bypass the compiler. Use type narrowing (typeof, instanceof, discriminated unions) instead.

Practice Questions

  1. What is the difference between interface and type? Interfaces can be extended, merged, and implemented by classes. Type aliases can represent unions, intersections, and primitives. Prefer interfaces for public API shapes.

  2. What does strict: true do in tsconfig? Enables all strict type-checking options: noImplicitAny, strictNullChecks, strictFunctionTypes, strictBindCallApply, and more.

  3. What is a generic? A way to create reusable components that work with any type. Array<T> is a generic — it works with any element type.

  4. How do you make a property optional? Add ? after the property name: age?: number.

  5. What does Pick<T, K> do? Creates a type from T by selecting only the properties listed in K. Pick<Todo, "title"> creates { title: string }.

Challenge: Write a TypeScript function that takes an array of objects and a key, and returns a Map grouping objects by that key’s value with full type safety.

Mini Project — Typed API Client

Build a type-safe HTTP client using generics and TypeScript’s type system.

interface ApiResponse<T> {
    data: T;
    status: number;
    ok: boolean;
}

class ApiClient {
    private baseUrl: string;

    constructor(baseUrl: string) {
        this.baseUrl = baseUrl;
    }

    async get<T>(endpoint: string): Promise<ApiResponse<T>> {
        const response = await fetch(`${this.baseUrl}${endpoint}`);
        const data = await response.json() as T;

        return {
            data,
            status: response.status,
            ok: response.ok
        };
    }

    async post<T, R>(endpoint: string, body: T): Promise<ApiResponse<R>> {
        const response = await fetch(`${this.baseUrl}${endpoint}`, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(body)
        });
        const data = await response.json() as R;

        return {
            data,
            status: response.status,
            ok: response.ok
        };
    }
}

// Usage
interface User {
    id: number;
    name: string;
    email: string;
}

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

async function main() {
    const api = new ApiClient("https://api.example.com");

    const users = await api.get<User[]>("/users");
    console.log(`Fetched ${users.data.length} users`);

    const newUser = await api.post<CreateUserRequest, User>(
        "/users",
        { name: "Alice", email: "alice@example.com" }
    );
    console.log(`Created user: ${newUser.data.name}`);
}

main().catch(console.error);

FAQ

Do I need to know JavaScript before TypeScript?
Yes. TypeScript is a superset of JavaScript. You need JavaScript fundamentals (variables, functions, objects, arrays, promises) to benefit from TypeScript’s type system.
Can I use TypeScript with React?
Yes — React has excellent TypeScript support. File extensions are .tsx instead of .tsx (though .tsx is standard). Types for React are included in @types/react.
What happens to types at runtime?
TypeScript types are erased during compilation. They exist only at compile time for checking. The output JavaScript has no type information.
Does TypeScript make code slower?
At runtime, no — TypeScript compiles to JavaScript. The compilation step adds a build-time cost, but the output JS runs at the same speed.
How do I add types for a JavaScript library?
Install @types/library-name from npm. These are type definition files that describe the library’s API. Many modern libraries include types directly.
What is the difference between TypeScript and JavaScript?
JavaScript is dynamically typed; TypeScript adds static typing. TypeScript compiles to JavaScript. All JavaScript code is valid TypeScript, but TypeScript catches type errors at compile time that JavaScript would only catch at runtime.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro