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.
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.
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.
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.
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.
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.
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.
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
- Enable Strict Mode: Use
"strict": trueintsconfig.jsonfor thorough type checking. - Rely on Inference: Annotate function parameters and public APIs; let the compiler infer the types of local variables.
- Avoid any: The
anytype turns off checking; useunknownor a specific type instead. - Describe Data with Interfaces: Give names to the shapes of objects that are passed around.
- Read Compiler Errors: They point to real bugs and often suggest the fix.
Source
This tutorial introduced the core features of TypeScript. The other tutorials in this series cover each topic in more detail.
Author
List all TypeScript tutorials.