ZetCode

TypeScript Basics

last modified October 15, 2026

TypeScript is JavaScript with static types. Every value and variable has a type, and the TypeScript compiler checks before the program runs that the types fit together. Errors such as calling a method that does not exist or passing a string where a number is expected are found in the editor instead of in production. The types are removed during compilation, and the result is plain JavaScript that runs in browsers and in Node.js.

The examples in this tutorial can be run with npx tsx file.ts; the setup tutorial shows how to install TypeScript and configure a project.

Variables and Types

A type annotation follows the variable name after a colon. The basic types are number, string, and boolean.

variables.ts
let age: number = 30;
const username: string = "Alice";
let isActive: boolean = true;

age = age + 1;

console.log(`${username} is ${age} years old.`);
console.log(`Active: ${isActive}`);

Variables declared with let can be reassigned, while const variables keep their initial value. JavaScript has a single number type for integers and floating-point numbers. Template literals, written with backticks, insert values into strings.

$ npx tsx variables.ts
Alice is 31 years old.
Active: true

Type Errors

The compiler rejects code that uses values in a way their types do not allow.

type_errors.ts
let age: number = 30;
age = "thirty";

const username: string = "Alice";
console.log(username.toUpperCase());
console.log(username.toUppercase());

The first error is an assignment of a string to a number variable. The second one is a typo in a method name; the compiler even suggests the correct name. In plain JavaScript, the first mistake would go unnoticed and the second would crash the program at runtime.

$ npx tsc --noEmit
type_errors.ts(2,1): error TS2322: Type 'string' is not assignable to type 'number'.
type_errors.ts(6,22): error TS2551: Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?

Type Inference

Annotations are often unnecessary. When a variable is initialized, TypeScript infers its type from the value.

inference.ts
let score = 95;
let items = ["Book", "Pen"];
const pi = 3.14159;

score = score + 5;
items.push("Notebook");

console.log(score, items, pi.toFixed(2));
// score = "Ninety-five";  // Error: Type 'string' is not assignable to type 'number'

The type of score is number and the type of items is string[], although neither was written. The inferred types are enforced just like annotations, so the commented line would not compile. Explicit annotations are mostly used for function parameters and where the type is not clear from the value. See the type inference tutorial.

$ npx tsx inference.ts
100 [ 'Book', 'Pen', 'Notebook' ] 3.14

Functions

Function parameters are annotated with types, and the return type is written after the parameter list.

functions.ts
function calculateArea(width: number, height: number): number {
    return width * height;
}

const greet = (name: string, greeting: string = "Hello"): string => {
    return `${greeting}, ${name}!`;
};

console.log(`Area: ${calculateArea(5, 4)}`);
console.log(greet("Bob"));
console.log(greet("Carol", "Good morning"));

The compiler checks every call: calculateArea("5", 4) or a call with a missing argument would be an error. The arrow function greet has a default value for its second parameter, so it can be called with one or two arguments. The return type can usually be inferred, but writing it documents the intent and catches mistakes inside the function.

$ npx tsx functions.ts
Area: 20
Hello, Bob!
Good morning, Carol!

Arrays and Union Types

An array type is written as the element type followed by []. A union type, written with |, allows a value to have one of several types.

arrays_unions.ts
const prices: number[] = [12.5, 8, 3.25];
const total = prices.reduce((sum, price) => sum + price, 0);

function formatId(id: string | number): string {
    if (typeof id === "number") {
        return `#${id.toString().padStart(4, "0")}`;
    }

    return id.toUpperCase();
}

console.log(`Total: ${total}`);
console.log(formatId(42));
console.log(formatId("ab-17"));

Only numbers can be added to prices, and the callback parameters of reduce are inferred as numbers. The formatId function accepts both strings and numbers. Inside the if block, the typeof check narrows the type to number; after it, only string is left, so toUpperCase is allowed.

$ npx tsx arrays_unions.ts
Total: 23.75
#0042
AB-17

Interfaces

An interface describes the shape of an object: the names and types of its properties.

interfaces.ts
interface User {
    id: number;
    name: string;
    email?: string;
}

function describe(user: User): string {
    const contact = user.email ?? "no e-mail";
    return `${user.id}: ${user.name} (${contact})`;
}

const user1: User = { id: 1, name: "Bob" };
const user2: User = { id: 2, name: "Eve", email: "eve@example.com" };

console.log(describe(user1));
console.log(describe(user2));

The question mark makes email optional, so user1 can omit it. Inside describe, its type is string | undefined, and the ?? operator supplies a default. An object with a missing name or with a misspelled property would be rejected. See the interfaces tutorial.

$ npx tsx interfaces.ts
1: Bob (no e-mail)
2: Eve (eve@example.com)

Classes

Classes add typed properties and access modifiers to JavaScript classes.

classes.ts
interface Describable {
    getDescription(): string;
}

class Vehicle implements Describable {
    constructor(public make: string, private model: string, private year: number) {}

    getDescription(): string {
        return `${this.year} ${this.make} ${this.model}`;
    }

    age(currentYear: number): number {
        return currentYear - this.year;
    }
}

const car = new Vehicle("Toyota", "Corolla", 2019);

console.log(car.getDescription());
console.log(car.make, car.age(2026));
// console.log(car.model);  // Error: Property 'model' is private

The constructor parameters with the public and private modifiers declare and initialize properties in one step. The public make property can be read from outside the class; the private model and year can only be used inside it. The implements Describable clause makes the compiler check that the class has a getDescription method. See the classes tutorial.

$ npx tsx classes.ts
2019 Toyota Corolla
Toyota 7

Best Practices

Source

The TypeScript Handbook

This tutorial introduced the core features of TypeScript. The other tutorials in this series cover each topic in more detail.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all TypeScript tutorials.