Coding · Programming languages
TypeScript tutorial for beginners
Short answer
TypeScript is JavaScript with static types. You add type annotations such as let count: number = 0, and the compiler tsc checks them before turning the code into plain JavaScript that runs in any browser or in Node.js. The key ideas are basic types, interfaces and type aliases for object shapes, union types with narrowing, and generics for reusable code. Types are erased at runtime.
What TypeScript is and what it is used for
TypeScript is an open-source language developed by Microsoft and first released in 2012. Every valid JavaScript program is also valid TypeScript, so you can adopt it file by file. The type checker catches mistakes such as misspelled properties or a missing null check while you type, and editors use the types for autocomplete and safe renaming. It is widely used for large front-end apps (Angular is written in it, and React, Vue and Svelte support it well), Node.js back ends and libraries.
Install and run Hello, world
Install Node.js first, then install the compiler in your project with npm. tsc compiles .ts files to .js.
const message: string = "Hello, world";
console.log(message);npm install --save-dev typescript
npx tsc --init # creates tsconfig.json
npx tsc hello.ts # writes hello.js
node hello.js
# Hello, worldRecent versions of Node.js, Deno and Bun can also run .ts files directly by removing the type annotations. They do not type-check, so still run npx tsc --noEmit to find type errors.
Basic types and type inference
You rarely need to write every type. When you initialize a variable, TypeScript infers its type: let n = 5 is a number. Annotate function parameters and public APIs.
| Type | Example | Notes |
|---|---|---|
number | let n: number = 3.5 | Integers and floats |
string | let s: string = "hi" | Text |
boolean | let ok: boolean = true | true or false |
| Array | number[] or Array<number> | Both forms mean the same |
| Tuple | [string, number] | Fixed length and positions |
unknown | let x: unknown | Any value, must be checked before use |
any | let y: any | Turns off checking; avoid |
void / never | function return types | Returns nothing / never returns |
let count = 0; // inferred as number
count = "ten"; // error: Type 'string' is not assignable to type 'number'
const names: string[] = ["Ann", "Bo"];
const pair: [string, number] = ["age", 31];The second line is a deliberate compile error, shown to illustrate what the checker reports.
Functions, control flow and narrowing
Control flow (if, for, while, switch) is exactly the same as in JavaScript. What TypeScript adds is narrowing: after a check such as typeof x === "string", the compiler knows the more specific type inside that branch.
function greet(name: string, greeting = "Hello"): string {
return `${greeting}, ${name}!`;
}
function formatId(id: string | number): string {
if (typeof id === "number") {
return id.toFixed(0).padStart(6, "0"); // id is number here
}
return id.toUpperCase(); // id is string here
}
console.log(greet("Lin")); // Hello, Lin!
console.log(formatId(42)); // 000042
console.log(formatId("ab7")); // AB7string | number is a union type: the value is one of the listed types. Optional parameters are written name?: string, and their type includes undefined.
Interfaces and type aliases
Describe the shape of objects with an interface or a type alias. TypeScript checks structure, not names: any object with the right properties fits.
interface User {
id: number;
name: string;
email?: string; // optional
readonly createdAt: Date;
}
type Role = "admin" | "editor" | "viewer"; // string literal union
function describe(user: User, role: Role): string {
const contact = user.email ?? "no email";
return `${user.name} (${role}, ${contact})`;
}
const ann: User = { id: 1, name: "Ann", createdAt: new Date() };
console.log(describe(ann, "editor")); // Ann (editor, no email)Signature features: generics and discriminated unions
Generics let one function or type work with many types while keeping them connected. A discriminated union is a union of object types that share a literal field, so a switch on that field narrows the type, and the compiler can check that every case is handled.
function first<T>(items: T[]): T | undefined {
return items[0];
}
const n = first([10, 20]); // n: number | undefined
const s = first(["a", "b"]); // s: string | undefined
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rect"; width: number; height: number };
function area(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "rect":
return shape.width * shape.height;
}
}
console.log(n, s, area({ kind: "rect", width: 2, height: 3 })); // 10 a 6Built-in utility types build new types from old ones: Partial<User> makes every property optional, Pick<User, "id" | "name"> keeps only some properties, and Record<string, number> describes a dictionary.
Error handling
Errors work as in JavaScript, with throw and try/catch. With the strict option on, the caught value has type unknown, because any value can be thrown, so check it before use.
function parseAge(text: string): number {
const age = Number(text);
if (!Number.isInteger(age) || age < 0) {
throw new Error(`invalid age: ${text}`);
}
return age;
}
try {
parseAge("abc");
} catch (err: unknown) {
const message = err instanceof Error ? err.message : String(err);
console.log(message); // invalid age: abc
}A small complete program: typed inventory
interface Item {
name: string;
price: number;
qty: number;
}
type Summary = { total: number; cheapest?: Item; outOfStock: string[] };
function summarize(items: Item[]): Summary {
let total = 0;
let cheapest: Item | undefined;
const outOfStock: string[] = [];
for (const item of items) {
total += item.price * item.qty;
if (item.qty === 0) outOfStock.push(item.name);
if (!cheapest || item.price < cheapest.price) cheapest = item;
}
return { total, cheapest, outOfStock };
}
const result = summarize([
{ name: "pen", price: 1.5, qty: 10 },
{ name: "book", price: 12, qty: 2 },
{ name: "bag", price: 30, qty: 0 },
]);
console.log(result.total, result.cheapest?.name, result.outOfStock);
// 39 pen [ 'bag' ]Try changing qty: 10 to qty: "10": the compiler reports the error before the code ever runs.
Common beginner mistakes
- Using
anyto silence errors. Preferunknownand narrow it. - Overusing
astype assertions, which tell the compiler to trust you instead of checking. - Leaving
strictoff intsconfig.json. Turn it on in new projects; it enablesnullchecks. - Writing types the compiler can already infer, which adds noise without safety.
Where to go next
Read the official TypeScript Handbook, and experiment in the TypeScript Playground on typescriptlang.org, which shows the compiled JavaScript and errors side by side. Then learn tsconfig.json options, type declaration files for libraries (@types/... packages), and TypeScript with your framework of choice.
FAQ
Is TypeScript a different language from JavaScript?
It is a superset of JavaScript: it adds type syntax and a checker, then compiles to plain JavaScript. Runtime behavior is the same as the JavaScript it produces.
Should I learn JavaScript before TypeScript?
Learn the JavaScript basics first (variables, functions, objects, promises), because TypeScript does not change them. Then add TypeScript; the types are easier to learn once the runtime behavior is familiar.
What is the difference between interface and type?
Both describe object shapes. Interfaces can be extended and merged by declaring them again; type aliases can also name unions, tuples and other non-object types. For plain object shapes either works.
Does TypeScript make my code faster?
No. Types are removed during compilation, so the output runs at the same speed as hand-written JavaScript. The benefit is fewer bugs and better tooling.