# Mastering TypeScript: Interfaces, Generics, Unions Explained

If JavaScript works, why was TypeScript created? This article answers that, then builds up from basic typing to interfaces, generics, and project configuration.

## 1\. Why TypeScript Exists

### Problems with Plain JavaScript in Large Applications

JavaScript doesn't check types. A function expecting a number happily accepts a string, an object with a missing property, or `undefined` — and the mistake only surfaces when that broken code actually runs, sometimes in production, sometimes deep in a call stack far from where the bug originated.

```javascript
function calculateTotal(price, quantity) {
  return price * quantity;
}

calculateTotal("100", 5); // "100100100100100" — no error, just wrong
```

In a small script, this is survivable. In an application with hundreds of files and multiple developers, silent type mismatches like this compound into real bugs.

### Runtime Errors vs Compile-Time Errors

*   **Runtime error**: caught only when the broken line executes — potentially after deployment.
    
*   **Compile-time error**: caught by the tool before the code ever runs.
    

```typescript
function calculateTotal(price: number, quantity: number) {
  return price * quantity;
}

calculateTotal("100", 5);
// Error: Argument of type 'string' is not assignable to parameter of type 'number'
```

TypeScript catches this the moment it's written, not after it ships.

### Benefits of Static Typing

*   Errors surface while coding, not in production
    
*   Autocomplete and inline documentation improve, since the editor knows exact shapes of data
    
*   Refactoring is safer — renaming a property flags every place it breaks
    
*   Code becomes self-documenting: function signatures describe what they expect
    

### How TypeScript Improves Developer Productivity

Less time debugging "why is this undefined," more time writing features. The editor catches typos in property names and wrong argument types before you even run the code.

### TypeScript as a Superset of JavaScript

Every valid JavaScript file is already valid TypeScript. TypeScript adds a type layer on top — it doesn't replace or reinvent the language.

```typescript
// Valid JS, also valid TS
let username = "DT89";
```

## 2\. Understanding Type Annotations

### Adding Types to Variables

```typescript
let age: number = 25;
let name: string = "Tamal";
let isActive: boolean = true;
```

### Function Parameter Types

```typescript
function greet(name: string) {
  console.log(`Hello, ${name}`);
}
```

### Function Return Types

```typescript
function add(a: number, b: number): number {
  return a + b;
}
```

### Type Inference

TypeScript often figures out the type without an annotation.

```typescript
let city = "Kolkata"; // inferred as string, no annotation needed
```

### Explicit vs Inferred Types

|  | Example | When to use |
| --- | --- | --- |
| Inferred | `let x = 10;` | Simple assignments where the value makes the type obvious |
| Explicit | `let x: number;` | Function parameters, return types, and empty declarations TypeScript can't guess |

## 3\. Interfaces vs Type Aliases

### What Interfaces Are

An interface describes the shape of an object.

```typescript
interface User {
  name: string;
  age: number;
}

const user1: User = { name: "Aditi", age: 22 };
```

### What Type Aliases Are

A type alias gives a name to any type — object shapes, unions, primitives.

```typescript
type User = {
  name: string;
  age: number;
};
```

### Similarities

Both describe object shapes and can be used interchangeably for basic cases like the `User` example above.

### Differences

|  | `interface` | `type` |
| --- | --- | --- |
| Extending | `extends` keyword | `&` (intersection) |
| Merging | Can be re-opened and merged | Cannot be redeclared |
| Unions | Cannot represent unions | Can represent unions directly |
| Best for | Object/class shapes | Unions, primitives, complex compositions |

### When to Use Interfaces

Modeling objects, especially ones that classes will implement:

```typescript
interface Product {
  id: number;
  name: string;
  price: number;
}
```

### When to Use Type Aliases

Anything involving unions or combining multiple types:

```typescript
type Status = "pending" | "shipped" | "delivered";
```

## 4\. Union Types

### What Union Types Are

A union type allows a value to be one of several specified types.

```typescript
let orderStatus: "pending" | "shipped" | "delivered";
orderStatus = "shipped"; // valid
```

### Combining Multiple Possible Types

```typescript
function printId(id: number | string) {
  console.log(`ID: ${id}`);
}

printId(101);
printId("A101");
```

### Real-World Use Cases

An `Order` might have a status that's only ever one of a fixed set of strings, or an API response that returns either a `User` or an `Error` object.

```typescript
interface Order {
  id: number;
  status: "pending" | "shipped" | "delivered";
}
```

### Handling Unions Safely

Narrow the type before using it, so TypeScript knows exactly which branch applies.

```typescript
function handleId(id: number | string) {
  if (typeof id === "number") {
    console.log(id.toFixed(2));
  } else {
    console.log(id.toUpperCase());
  }
}
```

## 5\. Intersection Types

### What Intersection Types Are

An intersection type combines multiple types into one — the result must satisfy all of them.

```typescript
type Name = { name: string };
type Age = { age: number };

type Person = Name & Age;

const p: Person = { name: "Rahul", age: 21 };
```

### Combining Multiple Type Definitions

```typescript
type Timestamped = { createdAt: Date };
type Product = { id: number; name: string };

type ProductWithTimestamp = Product & Timestamped;
```

### Creating Reusable Type Structures

Break shared fields into small types, then combine them wherever needed instead of repeating fields across every interface.

### Practical Example

```typescript
type BaseUser = { id: number; name: string };
type AdminPermissions = { canDeleteUsers: boolean; canBanUsers: boolean };

type Admin = BaseUser & AdminPermissions;

const admin: Admin = {
  id: 1,
  name: "Tamal",
  canDeleteUsers: true,
  canBanUsers: true,
};
```

## 6\. Generic Functions

### Why Generics Are Needed

Without generics, a reusable function either loses type safety (using `any`) or has to be duplicated per type.

```typescript
function identity(value: any): any {
  return value;
}
// Type safety is gone — value could be anything, output could be anything
```

### Reusable Type-Safe Functions

Generics let a function stay flexible while keeping the input and output types linked.

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

identity<number>(5); // T = number
identity<string>("hello"); // T = string
```

`T` is a placeholder type, filled in based on what's actually passed.

### Generic Parameters

```typescript
function getFirstElement<T>(arr: T[]): T {
  return arr[0];
}

getFirstElement<number>([1, 2, 3]); // 1
getFirstElement<string>(["a", "b"]); // "a"
```

### Generic Constraints

Restrict `T` to types that have certain properties, using `extends`.

```typescript
interface HasId {
  id: number;
}

function printId<T extends HasId>(item: T) {
  console.log(item.id);
}

printId({ id: 101, name: "Order A" }); // valid
printId({ name: "No ID here" }); // Error: missing 'id'
```

### Real-World Example

```typescript
function wrapInArray<T>(item: T): T[] {
  return [item];
}

wrapInArray<string>("Kolkata"); // ["Kolkata"]
wrapInArray<number>(42); // [42]
```

## 7\. Understanding tsconfig.json

### What tsconfig.json Is

A configuration file at the root of a TypeScript project that tells the compiler how to treat the code — which files to include, which JavaScript version to target, and how strict to be.

### Why TypeScript Projects Need It

Without it, every file would need compiler flags passed manually on every run. `tsconfig.json` centralizes those settings for the whole project.

### Common Compiler Options

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "strict": true,
    "outDir": "./dist",
    "rootDir": "./src"
  }
}
```

### Strict Mode

`"strict": true` enables a group of stricter checks — no implicit `any`, stricter null checks, and more — catching more mistakes at the cost of requiring more explicit types.

### Target Configuration

`"target"` decides which JavaScript version the TypeScript compiles down to (e.g., `ES5`, `ES2020`), based on which environments the code needs to run in.

### Module Configuration

`"module"` decides the module system used in the compiled output — `commonjs` for Node.js, `esnext` for modern bundlers.

### Project-Wide Settings

`include` and `exclude` control which files/folders the compiler processes:

```json
{
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
```

## 8\. TypeScript Compilation Process

### How TypeScript Becomes JavaScript

TypeScript code is passed through the TypeScript compiler (`tsc`), which strips out the types and produces plain JavaScript.

```plaintext
input.ts  --(tsc)-->  output.js
```

### What Happens During Compilation

1.  TypeScript checks all type annotations against how values are actually used
    
2.  If there are type errors, `tsc` reports them
    
3.  If everything checks out, `tsc` strips the types and emits plain JavaScript matching the configured `target`
    

### Why Browsers Cannot Run TypeScript Directly

Browsers only understand JavaScript. TypeScript's type system exists purely as a development-time tool — it has no meaning at runtime, so it must be compiled away before the code can execute anywhere.

### Build Workflow Overview

```plaintext
Write .ts files
      |
      v
Run tsc (or a bundler with a TS plugin)
      |
      v
Type-checking happens
      |
      v
.ts -> .js output (types stripped)
      |
      v
Browser / Node.js runs the plain JS
```

## Summary

| Concept | Core Idea |
| --- | --- |
| Type annotations | Declare expected types explicitly |
| Interfaces | Describe object shapes, support merging |
| Type aliases | Name any type, including unions |
| Union types | Value can be one of several types |
| Intersection types | Combine multiple types into one |
| Generics | Reusable functions that stay type-safe |
| tsconfig.json | Central compiler configuration |
| Compilation | `.ts` → type-checked → `.js` → runs in browser/Node |

Progression: basic typing → interfaces/type aliases → unions/intersections → generics → project-wide configuration. Each layer builds directly on the one before it.
