Frontend Development

The Definitive Guide to TypeScript: From Basics to Advanced Best Practices

By the Domain India teamPublished 8 min read
Knowledge base article
Contents (12 sections)

TypeScript is JavaScript with types. You write the same code you already know, add type annotations where they help, and the TypeScript compiler catches mistakes before your code runs. This guide takes you from setup to the features experienced developers rely on every day, with current 2026 practice, and ends with how to run a TypeScript app on Domain India hosting.

Key takeaways

Install TypeScript per project with npm install --save-dev typescript, create a tsconfig.json with strict turned on, and let tsc check and compile your code. Learn the core types, interfaces, unions, narrowing and generics first; they cover most real code. Current Node.js LTS releases can run .ts files directly by stripping the types, but they don't check them, so keep tsc --noEmit in your workflow. Compile to JavaScript before you deploy.

1. What TypeScript is and why teams use it

TypeScript was released by Microsoft in 2012 and is open source. It is a superset of JavaScript: every valid JavaScript file is valid TypeScript, and the compiler turns TypeScript back into plain JavaScript that runs in browsers and Node.js.

The types catch errors before runtime (a misspelt property, a missing argument, a possibly undefined value), power autocomplete and safe renames across the project, and make every function signature document what it expects and returns.

Types exist only at compile time. They are removed from the output, so they add nothing to the size or speed of your running code, and they cannot validate data arriving at runtime (see section 7).

2. Set up a project

You need Node.js. Use a current LTS release; check with node -v.

bash
mkdir my-app && cd my-app
npm init -y
npm install --save-dev typescript @types/node
npx tsc --init

Install TypeScript per project rather than globally, so every developer and your build server use the same compiler version. Run it with npx tsc. @types/node provides the types for Node's built-in modules.

3. Configure tsconfig.json

tsc --init writes a starting file. A sound configuration for a modern Node.js project looks like this:

json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "outDir": "dist",
    "rootDir": "src",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "verbatimModuleSyntax": true,
    "skipLibCheck": true,
    "sourceMap": true
  },
  "include": ["src"]
}

The options that matter most:

  • strict: true turns on the checks that make TypeScript worth using, including strict null checks. Start every new project with it.
  • module: nodenext makes TypeScript follow Node's real module rules, for both ES modules and CommonJS. Front-end projects built with a bundler use "module": "esnext" and "moduleResolution": "bundler" instead.
  • noUncheckedIndexedAccess treats array[i] as possibly undefined, which catches a common class of bugs.
  • outDir and rootDir keep source in src/ and compiled JavaScript in dist/.

Compile with npx tsc, or check without writing files using npx tsc --noEmit.

4. The basics

ts
let name: string = "Asha";
let age = 30;                 // inferred as number
const tags: string[] = ["new", "paid"];
let id: string | number = 42; // a union: either type

function greet(person: string, formal = false): string {
  return formal ? `Good morning, ${person}.` : `Hi ${person}!`;
}

Let TypeScript infer types for local variables, and write them out on function parameters, return types of exported functions, and data shapes. Avoid any: it switches checking off. When you don't know a type yet, use unknown, which forces you to check before use.

Interfaces and type aliases

ts
interface User {
  id: number;
  email: string;
  phone?: string;           // optional
  readonly createdAt: Date; // cannot be reassigned
}

type Status = "active" | "suspended" | "closed"; // literal union

Both describe the shape of an object. A common convention is interface for object shapes that may be extended, and type for unions and everything else.

Classes

ts
class Account {
  readonly owner: User;
  private balance = 0;

  constructor(owner: User) {
    this.owner = owner;
  }

  deposit(amount: number): void {
    if (amount <= 0) throw new Error("Amount must be positive");
    this.balance += amount;
  }
}

The fields are declared explicitly rather than with constructor parameter properties (constructor(public name: string)). That keeps the code compatible with tools that strip types without compiling, such as Node.js (section 8).

5. Narrowing: how TypeScript follows your checks

TypeScript tracks what you have checked and narrows the type inside each branch:

ts
function formatId(id: string | number): string {
  if (typeof id === "number") {
    return id.toFixed(0);   // id is number here
  }
  return id.toUpperCase();  // id is string here
}

Discriminated unions are the most useful pattern built on this. Give each variant a shared literal field and TypeScript checks that you handle every case:

ts
type Result =
  | { kind: "ok"; data: User }
  | { kind: "error"; message: string };

function show(r: Result): string {
  switch (r.kind) {
    case "ok":
      return r.data.email;
    case "error":
      return `Failed: ${r.message}`;
  }
}

If someone adds a third variant later, the compiler points at every switch that doesn't handle it.

6. Generics and utility types

Generics let a function or type work with many types while keeping them linked:

ts
function first<T>(items: T[]): T | undefined {
  return items[0];
}
const n = first([1, 2, 3]);     // number | undefined
const s = first(["a", "b"]);    // string | undefined

TypeScript ships utility types that transform existing ones, so you don't repeat yourself:

Utility typeWhat it producesTypical use
Partial of UserAll properties optionalUpdate or patch payloads
Pick of User, "id" or "email"Only the listed propertiesPublic views of a record
Omit of User, "createdAt"All except the listed propertiesData before it is saved
Record of Status, numberAn object with a key for each statusLookup tables and counters
ReturnType of a functionThe type the function returnsReusing a function's output type
ts
type UserUpdate = Partial<User>;
type PublicUser = Pick<User, "id" | "email">;

The satisfies operator checks a value against a type without widening it:

ts
const limits = { active: 10, suspended: 0, closed: 0 } satisfies Record<Status, number>;

7. Best practices

  • Keep strict on, and fix errors rather than silencing them with any or // @ts-ignore. If you must suppress one, // @ts-expect-error with a comment at least fails when the error goes away.
  • Validate data at the edges. Types vanish at runtime, so JSON from an API, a form or a database is unchecked. Parse it with a validation library such as Zod or Valibot, which can also produce the TypeScript type.
  • Prefer unions of string literals to enums. They are plain JavaScript values, work with type stripping and are easy to read.
  • Use import type for imports that are only types; with verbatimModuleSyntax the compiler enforces it.
  • Run the type check in CI. Add "typecheck": "tsc --noEmit" to package.json and run it on every push, alongside a linter such as typescript-eslint.

8. Running TypeScript

Compile, then run is the standard route for production:

json
"scripts": {
  "build": "tsc",
  "start": "node dist/index.js",
  "typecheck": "tsc --noEmit"
}

Run .ts directly during development. Current Node.js LTS releases can run a .ts file with node src/index.ts by stripping the type annotations. This works only for "erasable" syntax: enums, namespaces and constructor parameter properties are not supported. The TypeScript option "erasableSyntaxOnly": true flags them for you. Node does not type-check, so keep running tsc --noEmit. Tools such as tsx offer the same convenience with fewer restrictions.

9. Running a TypeScript app on Domain India

Whichever hosting you choose, deploy the compiled JavaScript, not the TypeScript source alone.

  • App Platform. A Node.js app is detected from package.json; give it a start script that runs the compiled file, and make your app listen on the PORT environment variable and bind to 0.0.0.0. Check the build output in the Deploys tab; if your dist folder is not being produced, commit the compiled files or build in a Dockerfile. See getting started with the App Platform.
  • Shared cPanel hosting. Setup Node.js App offers Node.js 20, 22 and 24 on our cPanel servers. Build with npm run build on your computer or in CI and upload the project with its dist folder; the full steps are in deploy a Node.js app on shared hosting.
  • VPS. A Domain India VPS is self-managed with full root access, so you install Node.js and run your app under a process manager yourself.
App Starter
₹100/mo + GST
  • 512 MB RAM per app
  • 1 vCPU
  • 5 GB NVMe SSD
  • PostgreSQL Database
See plan details

Frequently asked questions

What is TypeScript?

TypeScript is an open-source superset of JavaScript, released by Microsoft in 2012, that adds optional static types. The TypeScript compiler checks your code for type errors and compiles it to plain JavaScript that runs in browsers and Node.js.

Should I install TypeScript globally?

It is better to install it per project with npm install --save-dev typescript and run it with npx tsc. That way every developer and the build server use the same compiler version.

What does strict mode do in tsconfig.json?

strict: true turns on TypeScript's stricter checks, including strict null checks, which stop you using a value that might be null or undefined without checking it first. It is recommended for every new project.

Can Node.js run TypeScript files directly?

Current Node.js LTS releases can run a .ts file by stripping its type annotations, but they do not type-check the code and do not support enums, namespaces or constructor parameter properties. Keep running tsc --noEmit to check types.

Does TypeScript check data at runtime?

No. Types are removed when the code is compiled, so data from APIs, forms or databases is not checked at runtime. Validate it with a library such as Zod or Valibot.

What is the difference between interface and type?

Both can describe the shape of an object. Interfaces can be extended and merged, while type aliases can also describe unions, literal types and other combinations. Many teams use interface for object shapes and type for everything else.

Can I host a TypeScript app on Domain India?

Yes. Compile it to JavaScript and deploy it on the App Platform, which detects Node.js apps from package.json, with Setup Node.js App on cPanel shared hosting, or on a self-managed VPS.

Ready to deploy? Start with getting started with the App Platform, or see deploy a Node.js app on shared hosting if you already have cPanel hosting. Plans are on the App Platform page.

Deploy your TypeScript app

Push your code or upload it with a deploy token, and the App Platform builds it, runs it in its own container and serves it over HTTPS.

See App Platform plans

Was this article helpful?

Your answer helps us decide what to improve next.

Still need help? Open a support ticket and our team will reply.

Prefer an app? Add this site to your home screen.Get the app
TypeScript guide: from basics to best practices 2026