> ## Content Index
> Fetch the complete content index at: https://sudosecurity.org/llms.txt
> Use this file to discover other available public pages before exploring further.

# What is TypeScript? Why I Finally Stopped Fighting the Type System
- URL: https://sudosecurity.org/what-is-typescript/
- Published: 2025-07-02T05:35:00.000Z
- Updated: 2026-07-17T10:50:16.000Z
- Description: TypeScript is JavaScript with types. That's the short answer. The real answer involves a friend who was right about something I didn't want to hear, a production bug that types would have caught immediately, and the slow realization that the compiler was never the enemy.
- Author: Traven
- Tags: Web Development, JavaScript, TypeScript, Programming Languages, Programming

A friend of mine had been on my case about TypeScript since around 2018\. Every time I showed him JavaScript code, he'd go through the same routine: "Why aren't you using TypeScript? Just use TypeScript." And every time, I had the same answer: "I don't need the compiler telling me what to do. I know what my variables are."

I was wrong. I knew I was wrong the moment a production Discord bot crashed because a function returned `undefined` when I expected an object, and I spent two hours hunting down which property access on a null reference was the culprit. That bug would not have made it past TypeScript's compiler. The type checker would have flagged it in my editor before I even ran the code.

I converted the bot to TypeScript that week. I haven't shipped a serious JavaScript project without it since.

## What TypeScript Is

TypeScript is a superset of JavaScript developed by Microsoft, first released in 2012\. Every valid JavaScript file is valid TypeScript. The language adds optional static type annotations, a type inference engine, and a compiler that strips those types and outputs standard JavaScript.

The output is JavaScript. TypeScript never runs in the browser or in Node.js directly. The compiler (`tsc`) or a bundler like esbuild processes your TypeScript and produces the JavaScript that actually executes.

This matters for one reason: TypeScript's types exist only at compile time. Once the code runs, they're gone. There's no runtime type checking. The type system is a development-time safety net, not a runtime enforcement mechanism.

## The Type System

### Basic Types

TypeScript's primitive types map directly to JavaScript's: `string`, `number`, `boolean`, `null`, `undefined`, `symbol`, `bigint`. You annotate variables, function parameters, and return values with these types.

```typescript
let name: string = "Traven";
let age: number = 30;
let active: boolean = true;

function greet(name: string): string {
  return `Hello, ${name}`;
}

```

TypeScript can often infer the type without the annotation:

```typescript
let name = "Traven"; // TypeScript knows this is a string

```

The annotation is optional when TypeScript can figure it out. Explicit annotations are most useful in function signatures and complex object shapes where the inference would be ambiguous.

### Arrays and Objects

Arrays take a type parameter for their elements. Object shapes are defined inline or through interfaces.

```typescript
const scores: number[] = [95, 87, 92];

// Inline object type
const user: { name: string; age: number } = {
  name: "Traven",
  age: 30
};

```

### Interfaces

Interfaces define the shape of an object. They're reusable and composable.

```typescript
interface User {
  id: number;
  name: string;
  email: string;
  role: "admin" | "editor" | "viewer";
}

function sendWelcomeEmail(user: User): void {
  console.log(`Sending to ${user.email}`);
}

```

The `"admin" | "editor" | "viewer"` is a union type. The `role` property must be one of those three exact strings. TypeScript will reject anything else at compile time.

### Type Aliases

`type` creates an alias for any type, including unions, intersections, and complex generics.

```typescript
type UserId = number;
type UserRole = "admin" | "editor" | "viewer";

type ApiResponse<T> = {
  data: T;
  status: number;
  error: string | null;
};

const response: ApiResponse<User[]> = {
  data: [],
  status: 200,
  error: null
};

```

`ApiResponse<T>` is a generic type. The `T` is a type parameter, filled in when you use the type. `ApiResponse<User[]>` means the `data` field will be an array of `User` objects.

### Interfaces vs Types

Both `interface` and `type` define object shapes. The practical difference: interfaces can be extended with `extends` and merged through declaration merging. Types are more flexible for non-object type aliases (unions, intersections, mapped types).

For object shapes, use interfaces. For everything else, use types. That's not a hard rule, but it's a reasonable default that most TypeScript codebases follow.

### Optional and Readonly Properties

```typescript
interface Config {
  host: string;
  port: number;
  timeout?: number;      // optional
  readonly apiKey: string; // can't be reassigned after creation
}

```

### Generics

Generics let you write functions and classes that work with any type while preserving type information.

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

const firstNumber = first([1, 2, 3]); // TypeScript knows this is number | undefined
const firstString = first(["a", "b"]); // TypeScript knows this is string | undefined

```

Without generics, you'd have to use `any` and lose all type safety, or write separate functions for every type you wanted to support.

### The `unknown` Type

`unknown` is the type-safe alternative to `any`. A value of type `unknown` can hold anything, but TypeScript won't let you do anything with it until you narrow the type.

```typescript
function processInput(input: unknown) {
  if (typeof input === "string") {
    console.log(input.toUpperCase()); // TypeScript knows it's a string here
  } else if (typeof input === "number") {
    console.log(input.toFixed(2)); // TypeScript knows it's a number here
  }
}

```

Use `unknown` when you receive data from an external source (API response, user input, `JSON.parse`) and want to handle it safely.

## The tsconfig.json File

The `tsconfig.json` file at the root of your project controls compiler behavior. The most important setting is `strict`.

```json
{
  "compilerOptions": {
    "strict": true,
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true,
    "sourceMap": true
  },
  "include": ["src/**/*"]
}

```

`"strict": true` enables a bundle of strict type-checking options. The most impactful are `strictNullChecks` (which makes `null` and `undefined` their own types instead of assignable to everything) and `noImplicitAny` (which forces you to annotate when TypeScript can't infer a type).

Run TypeScript without `strict: true` and you're leaving most of the protection on the table.

## Real-World Usage

### Typing API Responses

The biggest practical win from TypeScript in production code is typing external data.

```typescript
interface GitHubUser {
  login: string;
  id: number;
  avatar_url: string;
  name: string | null;
  email: string | null;
  public_repos: number;
}

async function fetchUser(username: string): Promise<GitHubUser> {
  const res = await fetch(`https://api.github.com/users/${username}`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json() as GitHubUser;
}

```

The `as GitHubUser` cast tells TypeScript to treat the parsed JSON as a `GitHubUser`. TypeScript trusts you here, so you're responsible for making sure the interface matches the actual API response. If you want runtime validation too, use a library like Zod alongside the TypeScript types.

### Discriminated Unions

Discriminated unions are one of TypeScript's most powerful patterns. You use a shared property (the discriminant) to narrow which member of a union you're dealing with.

```typescript
type ApiResult<T> =
  | { success: true; data: T }
  | { success: false; error: string };

function handleResult(result: ApiResult<User>) {
  if (result.success) {
    // TypeScript knows result.data exists here
    console.log(result.data.name);
  } else {
    // TypeScript knows result.error exists here
    console.log(result.error);
  }
}

```

This pattern eliminates the `data?.name` optional chaining dance and makes impossible states unrepresentable.

### Classes with TypeScript

TypeScript adds visibility modifiers to classes: `public`, `private`, `protected`, and `readonly`.

```typescript
class DatabaseConnection {
  private readonly connectionString: string;
  private connected: boolean = false;

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

  async connect(): Promise<void> {
    // connect logic
    this.connected = true;
  }

  get isConnected(): boolean {
    return this.connected;
  }
}

```

The `private` keyword is enforced at compile time. For runtime enforcement, use JavaScript's native `#privateField` syntax, which TypeScript also supports.

## Gradual Adoption

You don't have to convert an entire codebase at once. TypeScript supports gradual adoption through the `allowJs` compiler option, which lets you mix `.ts` and `.js` files in the same project. You can rename files from `.js` to `.ts` one at a time, fixing type errors as you go.

The `@ts-check` comment at the top of a `.js` file enables basic type checking through JSDoc comments without converting to TypeScript:

```javascript
// @ts-check

/**
 * @param {string} name
 * @returns {string}
 */
function greet(name) {
  return `Hello, ${name}`;
}

```

This works, but it's verbose compared to actual TypeScript syntax. Use it as a stepping stone, not a destination.

## The Build Step

TypeScript requires compilation. In a Node.js project, you're running `tsc` or using a bundler with TypeScript support. In a browser project, your bundler (Vite, Webpack, esbuild) handles the TypeScript compilation as part of the build.

For development speed, tools like `ts-node` and `tsx` let you execute TypeScript directly without an explicit compile step. `tsx` is faster and handles ESM correctly.

Deno and Bun both execute TypeScript natively without a separate compilation step. If you're on one of those runtimes, the build overhead disappears. More on that in the [What is Deno](https://sudosecurity.org/what-is-deno/) and [What is Bun](https://sudosecurity.org/what-is-bun/) articles.

## Where TypeScript Doesn't Help

TypeScript's types are erased at runtime. `JSON.parse()` returns `any`. External API responses don't match your interface definitions at runtime unless you validate them explicitly. Type assertions (`as SomeType`) trust you completely.

The type system can't guarantee that runtime values match your compile-time types. You bridge that gap with runtime validation — Zod is the current standard, and it generates TypeScript types from schemas, keeping your compile-time and runtime validation in sync.

TypeScript also adds setup friction to small scripts. A ten-line utility script probably doesn't need a `tsconfig.json`. For quick work, JavaScript is fine. For anything you're shipping, TypeScript pays for itself quickly.

## Compared to JavaScript

If you want the full comparison between JavaScript and TypeScript, including when each is the right choice, that's in the [JavaScript vs TypeScript article](https://sudosecurity.org/javascript-vs-typescript/).

The short version: TypeScript is JavaScript with a safety net. The net costs you a build step and some upfront annotation work. In exchange, your editor catches bugs before you run anything, refactoring is safer, and reading unfamiliar code takes less time because the types document intent.

My friend was right. I was stubborn. Use TypeScript.

## Where to Go From Here

TypeScript builds on JavaScript. If your JavaScript fundamentals aren't solid, especially the async model, [start with the JavaScript article](https://sudosecurity.org/what-is-javascript/) and [the event loop deep dive](https://sudosecurity.org/javascript-event-loop/).

For practical TypeScript in a real project, the [Discord bot guide](https://sudosecurity.org/js-discord-bot-simplified-guide/) uses Node.js, TypeScript, and the Eris library to build something functional from scratch. That's where I'd start if you want to see TypeScript in a working application rather than isolated examples.