What Is TypeScript
TypeScript is a language that adds a static type system to JavaScript. By catching type errors at compile time, it prevents bugs that would otherwise surface at runtime. It plays a critical role in improving code stability and maintainability in large-scale projects.
This article covers TypeScript installation, tsconfig.json configuration, basic types, function types, and interfaces.
Installation and Project Setup
TypeScript can be installed globally or per-project in a Node.js environment.
# Install TypeScript
npm init -y
npm install --save-dev typescript
# Generate tsconfig.json
npx tsc --init
Let’s look at the key options in the generated tsconfig.json.
{
"compilerOptions": {
// Target JavaScript version
"target": "ES2022",
// Module system
"module": "ESNext",
// Module resolution strategy
"moduleResolution": "bundler",
// Enable strict mode (highly recommended)
"strict": true,
// Output directory
"outDir": "./dist",
// Generate source maps (for debugging)
"sourceMap": true,
// Allow .js files
"allowJs": true,
// ESModule interop
"esModuleInterop": true,
// Error on unused local variables
"noUnusedLocals": true,
// Error on unused parameters
"noUnusedParameters": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
strict: true enables multiple options at once, including strictNullChecks, noImplicitAny, and strictFunctionTypes. It is strongly recommended to enable this for new projects.
Basic Types
TypeScript’s basic types correspond to JavaScript’s primitive types. Specify the type after a colon (:) when declaring variables.
// Basic primitive types
const name: string = "Alice";
const age: number = 30;
const isActive: boolean = true;
// Array types (two notations)
const scores: number[] = [95, 87, 92];
const names: Array<string> = ["Alice", "Bob"];
// Tuple: fixed length, each element can have a different type
const user: [string, number] = ["Alice", 30];
// enum: a group of related constants
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
const move: Direction = Direction.Up;
console.log(move); // "UP"
// any: disables type checking (avoid using whenever possible)
let flexible: any = "string";
flexible = 42; // No error — type safety is lost
// unknown: a safer alternative to any
let uncertain: unknown = "data";
// uncertain.toUpperCase(); // Error! Type narrowing required
if (typeof uncertain === "string") {
console.log(uncertain.toUpperCase()); // "DATA" — usable after type narrowing
}
The difference between any and unknown is important. unknown requires you to narrow the type before use, making it safer than any when working with external data.
Function Types
Specifying parameter and return types for functions lets you immediately catch type mismatches at call sites.
// Basic function type definition
function add(a: number, b: number): number {
return a + b;
}
console.log(add(3, 5)); // 8
// Optional parameters (?) and default values
function greet(name: string, greeting: string = "Hello"): string {
return `${greeting}, ${name}!`;
}
console.log(greet("Alice")); // "Hello, Alice!"
console.log(greet("Bob", "Welcome")); // "Welcome, Bob!"
// Rest parameters
function sum(...numbers: number[]): number {
return numbers.reduce((acc, cur) => acc + cur, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
// Arrow function type
const multiply: (a: number, b: number) => number = (a, b) => a * b;
console.log(multiply(4, 7)); // 28
// void: function with no return value
function logMessage(message: string): void {
console.log(`[LOG] ${message}`);
}
// never: function that never returns (throws or infinite loop)
function throwError(message: string): never {
throw new Error(message);
}
Interfaces and Type Aliases
Use interface and type to define object structures. Both are similar, but interface supports declaration merging while type allows more flexible compositions like unions and intersections.
// interface: define object structure
interface User {
id: number;
name: string;
email: string;
age?: number; // Optional property
readonly createdAt: Date; // Read-only
}
// Create an object using the interface
const user: User = {
id: 1,
name: "Alice",
email: "alice@example.com",
createdAt: new Date(),
};
// type alias: union type
type Status = "active" | "inactive" | "pending";
// type alias: object type
type ApiResponse<T> = {
data: T;
status: number;
message: string;
};
// Interface extension
interface Admin extends User {
role: "admin" | "superadmin";
permissions: string[];
}
const admin: Admin = {
id: 2,
name: "Admin",
email: "admin@example.com",
createdAt: new Date(),
role: "admin",
permissions: ["read", "write", "delete"],
};
// Interface declaration merging
interface User {
nickname?: string; // Property added to existing User
}
Type Narrowing
TypeScript automatically narrows types through conditional statements. This is called a type guard.
// typeof guard
function formatValue(value: string | number): string {
if (typeof value === "string") {
return value.toUpperCase(); // Narrowed to string
}
return value.toFixed(2); // Narrowed to number
}
console.log(formatValue("hello")); // "HELLO"
console.log(formatValue(3.14159)); // "3.14"
// in operator guard
interface Dog {
breed: string;
bark(): void;
}
interface Cat {
breed: string;
meow(): void;
}
function handlePet(pet: Dog | Cat): void {
if ("bark" in pet) {
pet.bark(); // Narrowed to Dog
} else {
pet.meow(); // Narrowed to Cat
}
}
// Custom type guard (is keyword)
function isString(value: unknown): value is string {
return typeof value === "string";
}
const data: unknown = "TypeScript";
if (isString(data)) {
console.log(data.length); // 10 — narrowed to string
}
Summary
Here are the key things to remember when getting started with TypeScript.
- tsconfig.json: Always enable
strict: true. It is the foundation of type safety. - Basic types (string, number, boolean, arrays, tuples, enums) correspond to JavaScript’s primitive types.
- Use unknown instead of any, and access values safely through type narrowing.
- Functions should have explicit parameter types and return types.
- interface is ideal for defining and extending object structures; type is better for unions and compositions.
- Type guards provide both runtime safety and IDE autocomplete simultaneously.