TypeScript checks your JavaScript before it runs. Wrong data types show as errors while you code, not when users hit your app.
Syntax: let name: type = value — a named box that must hold that type.
Core Types
A type answers: what kind of value is this?
String — text.
let message: string = "Hello, TypeScript!";
Number — whole or decimal.
let age: number = 30;
let pi: number = 3.14159;
Boolean — only true or false.
let isAuthenticated: boolean = true;
let hasPermission: boolean = false;
Array — list of one kind. string[] or Array<boolean>.
let numbers: number[] = [1, 2, 3, 4, 5];
let names: string[] = ["Alice", "Bob", "Charlie"];
let flags: Array<boolean> = [true, false, true];
Tuple — fixed order per slot. Wrong order = error.
let user: [number, string, boolean];
user = [1, "Alice", true];
// user = ["Alice", 1, true]; // Error
Enum — named choices instead of magic numbers.
enum Role { Admin, User, Guest }
let role: Role = Role.Admin;
enum Status { Active = 1, Inactive = 0, Suspended = -1 }
let accountStatus: Status = Status.Active;
Final code:
let message: string = "Hello, TypeScript!";
let age: number = 30;
let pi: number = 3.14159;
let isAuthenticated: boolean = true;
let hasPermission: boolean = false;
let numbers: number[] = [1, 2, 3, 4, 5];
let names: string[] = ["Alice", "Bob", "Charlie"];
let flags: Array<boolean> = [true, false, true];
let user: [number, string, boolean];
user = [1, "Alice", true];
// user = ["Alice", 1, true]; // Error: order doesn't match
enum Role { Admin, User, Guest }
let role: Role = Role.Admin;
enum Status { Active = 1, Inactive = 0, Suspended = -1 }
let accountStatus: Status = Status.Active;
Arrays
An array holds multiple values. type[] means every item is that type.
String array:
let fruits: string[] = ["Apple", "Banana", "Cherry"];
Number array:
let scores: number[] = [90, 85, 78];
Boolean array:
let isAvailable: boolean[] = [true, false, true];
Array of objects — each item has the same shape:
let users: { id: number; name: string; active: boolean }[] = [
{ id: 1, name: "Alice", active: true },
{ id: 2, name: "Bob", active: false },
];
Mixed array — uses any[], turns off type checking. Avoid when you can.
let mixed: any[] = ["Text", 42, true];
Final code:
let fruits: string[] = ["Apple", "Banana", "Cherry"];
let scores: number[] = [90, 85, 78];
let isAvailable: boolean[] = [true, false, true];
let users: { id: number; name: string; active: boolean }[] = [
{ id: 1, name: "Alice", active: true },
{ id: 2, name: "Bob", active: false },
];
let mixed: any[] = ["Text", 42, true];
Objects
An object groups related fields. Access with dot: car.brand.
Simple object:
let car: { brand: string; model: string; year: number } = {
brand: "Tesla", model: "Model S", year: 2023,
};
Nested object — object inside object:
let employee: {
id: number; name: string; position: string;
contact: { email: string; phone: string };
} = {
id: 101, name: "John Doe", position: "Software Engineer",
contact: { email: "john.doe@example.com", phone: "123-456-7890" },
};
Optional field — ? means the field may be missing:
let product: { id: number; name: string; price?: number } = {
id: 1, name: "Laptop",
};
Final code:
let car: { brand: string; model: string; year: number } = {
brand: "Tesla", model: "Model S", year: 2023,
};
let employee: {
id: number; name: string; position: string;
contact: { email: string; phone: string };
} = {
id: 101, name: "John Doe", position: "Software Engineer",
contact: { email: "john.doe@example.com", phone: "123-456-7890" },
};
let product: { id: number; name: string; price?: number } = {
id: 1, name: "Laptop",
};
Functions
A function runs reusable steps. Type the inputs and the return value.
Basic function — two numbers in, one number out:
function add(a: number, b: number): number {
return a + b;
}
Optional parameter — ? means you can skip it:
function greet(name: string, message?: string): string {
return message ? `${message}, ${name}!` : `Hello, ${name}!`;
}
Default value — used when the argument is not passed:
function calculateDiscount(price: number, discount: number = 10): number {
return price - (price * discount) / 100;
}
Return an object:
function createUser(id: number, name: string): { id: number; name: string; active: boolean } {
return { id, name, active: true };
}
Arrow function — short syntax with =>:
const multiply = (a: number, b: number): number => a * b;
Return undefined when nothing is found:
let employees: { id: number; name: string; role: string }[] = [
{ id: 1, name: "Alice", role: "Developer" },
{ id: 2, name: "Bob", role: "Manager" },
];
function findEmployeeById(id: number): { id: number; name: string; role: string } | undefined {
return employees.find((employee) => employee.id === id);
}
Final code:
function add(a: number, b: number): number { return a + b; }
function greet(name: string, message?: string): string {
return message ? `${message}, ${name}!` : `Hello, ${name}!`;
}
function calculateDiscount(price: number, discount: number = 10): number {
return price - (price * discount) / 100;
}
function createUser(id: number, name: string): { id: number; name: string; active: boolean } {
return { id, name, active: true };
}
const multiply = (a: number, b: number): number => a * b;
let employees: { id: number; name: string; role: string }[] = [
{ id: 1, name: "Alice", role: "Developer" },
{ id: 2, name: "Bob", role: "Manager" },
];
function findEmployeeById(id: number): { id: number; name: string; role: string } | undefined {
return employees.find((employee) => employee.id === id);
}
Type Inference
TypeScript infers types from values — you write less, stay safe.
Object inference — types are detected from the value:
const userInfo = { id: 1, name: 'MD Hemal Akhand', age: 25, verified: true };
typeof — reuse an object's shape as a type:
function printUser(input: typeof userInfo) {
console.log(input.name);
}
Inside a function — destructured fields are inferred too:
function validateForm(formData: { email: string; age: number; isAdmin: boolean }) {
const { email, age, isAdmin } = formData;
return email.includes('@') && age < 18;
}
Final code:
const userInfo = { id: 1, name: 'MD Hemal Akhand', age: 25, verified: true };
function printUser(input: typeof userInfo) {
console.log(input.name);
}
function validateForm(formData: { email: string; age: number; isAdmin: boolean }) {
const { email, age, isAdmin } = formData;
return email.includes('@') && age < 18;
}
Type Aliases
A type alias names a shape once and reuses it.
Simple alias:
type name = string;
type ID = string | number;
Use the alias:
function printID(id: ID) { console.log(`Your id is: ${id}`); }
printID(123);
printID('123');
Object alias:
type User = { id: ID; firstName: string; lastName: string; skills: string[] };
Return type on function:
function createUser(firstName: string, lastName: string): User {
return { id: crypto.randomUUID(), firstName, lastName, skills: [] };
}
satisfies — checks shape without forcing return type:
function createUser2(firstName: string, lastName: string) {
return { id: crypto.randomUUID(), firstName, lastName, skills: [] } satisfies User;
}
as — forces a type. Skips checks. Not recommended.
function createUser3(firstName: string, lastName: string) {
return { id: crypto.randomUUID(), firstName, lastName } as User;
}
Indexed access — pull nested types from a type:
type UserTwo = {
id: ID; firstName: string; lastName: string; skills: string[];
address: { street: string; city: string; country: string;
coordinats: { lat: number; long: number } };
};
type Address = UserTwo['address'];
type Skill = UserTwo['skills'][number];
Callback types — keep data types and function types separate:
type CB = () => void;
type CB2 = (arg1: string, arg2: number) => string;
Final code:
type name = string;
type ID = string | number;
function printID(id: ID) { console.log(`Your id is: ${id}`); }
printID(123);
printID('123');
type User = { id: ID; firstName: string; lastName: string; skills: string[] };
function createUser(firstName: string, lastName: string): User {
return { id: crypto.randomUUID(), firstName, lastName, skills: [] };
}
function createUser2(firstName: string, lastName: string) {
return { id: crypto.randomUUID(), firstName, lastName, skills: [] } satisfies User;
}
function createUser3(firstName: string, lastName: string) {
return { id: crypto.randomUUID(), firstName, lastName } as User; // not recommended
}
type UserTwo = {
id: ID; firstName: string; lastName: string; skills: string[];
address: { street: string; city: string; country: string;
coordinats: { lat: number; long: number } };
};
type Address = UserTwo['address'];
type Coordinats = UserTwo['address']['coordinats'];
type CoordinatsTwo = Address['coordinats'];
type Skill = UserTwo['skills'][number];
type CB = () => void;
type CB2 = (arg1: string, arg2: number) => string;
function printAddress(addr: Address) { console.log(addr.country); }
function testCB(cb: CB) { cb(); }
function testCB2(cb: CB2) { cb('hemal', 27); }
Union Types
A union means this OR that — string | number.
Union alias:
type MyID = string | number;
Discriminated union — shared role field narrows the type in if:
type Admin = { id: MyID; role: 'admin'; fullControl: true };
type Manager = { id: MyID; role: 'manager' };
type MyUser = Admin | Manager;
function doSomething(user: MyUser) {
if (user.role === 'admin') console.log(`Full control: ${user.fullControl}`);
else console.log('Role: ' + user.role);
}
typeof narrows primitive unions:
function formatValue(value: string | number): string {
if (typeof value === 'string') return value.toUpperCase();
return value.toFixed(2);
}
Success or error response:
type ErrorResponse = { error: true; message: string };
type SuccessResponse = { error: false; data: string };
type APIResponse = ErrorResponse | SuccessResponse;
function handleResponse(response: APIResponse) {
if (response.error) console.log(response.message);
else console.log(response.data);
}
Final code:
type MyID = string | number;
type Admin = { id: MyID; role: 'admin'; fullControl: true };
type Manager = { id: MyID; role: 'manager' };
type MyUser = Admin | Manager;
function doSomething(user: MyUser) {
if (user.role === 'admin') console.log(`Full control: ${user.fullControl}`);
else console.log('Role: ' + user.role);
}
function formatValue(value: string | number): string {
if (typeof value === 'string') return value.toUpperCase();
return value.toFixed(2);
}
console.log(formatValue(30));
console.log(formatValue('My Name'));
type ErrorResponse = { error: true; message: string };
type SuccessResponse = { error: false; data: string };
type APIResponse = ErrorResponse | SuccessResponse;
function handleResponse(response: APIResponse) {
if (response.error) console.log(response.message);
else console.log(response.data);
}
Intersection Types
An intersection means this AND that — merge types with &.
Person + role fields:
type Person = { id: ID; name: string; age: number };
type Employee = Person & { role: 'employee'; salary: number };
type Customer = Person & { role: 'customer'; balance: number };
Use the merged type:
const newCustomer: Customer = {
id: 1, name: 'hemal', age: 27, role: 'customer', balance: 30,
};
Shared base + extra props (common in UI components):
type BaseProps = { id: number; className?: string };
type buttonProps = BaseProps & { label: string; onCLick: () => void };
type inputBox = BaseProps & { value: string; onChange: () => void; placeholder: string };
Final code:
type Person = { id: ID; name: string; age: number };
type Employee = Person & { role: 'employee'; salary: number };
type Customer = Person & { role: 'customer'; balance: number };
const newCustomer: Customer = {
id: 1, name: 'hemal', age: 27, role: 'customer', balance: 30,
};
type BaseProps = { id: number; className?: string };
type buttonProps = BaseProps & { label: string; onCLick: () => void };
type inputBox = BaseProps & { value: string; onChange: () => void; placeholder: string };
Generics
Generics use a type placeholder T — one pattern, many types.
Generic function — same input type in, same type out:
function functionName<T>(value: T): T {
console.log(value, typeof value);
return value;
}
functionName<number>(30);
functionName('30');
Merge two objects:
function mergeObject<T, U>(arg1: T, arg2: U): T & U {
return { ...arg1, ...arg2 };
}
const mergeObj = mergeObject({ a: 1 }, { b: 2 });
const mergeObj2 = mergeObject({ name: 'Hemal', age: 27 }, { birthday: 1999 });
Generic type — box that holds any type:
type MystryBox<T> = { value: T };
const numberBox: MystryBox<number> = { value: 123 };
API wrapper — same response shape, different data:
type ApiResponse<T> = { data: T; status: number; message: string };
async function fetchUser(): Promise<ApiResponse<UserGenericsType>> {
const response = await fetch('https://www.example.com/users');
const data = await response.json();
return { data, status: response?.status, message: response?.statusText };
}
One fetch function for any endpoint:
const fetchData = async <T>(url: string): Promise<ApiResponse<T>> => {
const response = await fetch(url);
const data = await response.json();
return { data, status: response?.status, message: response?.statusText };
};
Final code:
function functionName<T>(value: T): T {
console.log(value, typeof value);
return value;
}
functionName<number>(30);
functionName('30');
function mergeObject<T, U>(arg1: T, arg2: U): T & U {
return { ...arg1, ...arg2 };
}
function mergeObject2<T, U>(arg1: T, arg2: U) {
return { ...arg1, ...arg2 };
}
const mergeObj = mergeObject({ a: 1 }, { b: 2 });
const mergeObj2 = mergeObject({ name: 'Hemal', age: 27 }, { birthday: 1999 });
type MystryBox<T> = { value: T };
const numberBox: MystryBox<number> = { value: 123 };
type UserGenericsType = { id: ID; firstName: string; lastName: string; skills: string[] };
const userBox: MystryBox<UserGenericsType | null> = { value: null };
if (userBox.value) console.log(userBox.value.firstName);
type ApiResponse<T> = { data: T; status: number; message: string };
type Product = { id: string | number; name: string; price: number; discount: number };
async function fetchUser(): Promise<ApiResponse<UserGenericsType>> {
const response = await fetch('https://www.example.com/users');
const data = await response.json();
return { data, status: response?.status, message: response?.statusText };
}
async function syncProduct(): Promise<ApiResponse<Product>> {
const response = await fetch('https://www.example.com/products');
const data = await response.json();
return { data, status: response?.status, message: response?.statusText };
}
async function main() {
const user = await fetchUser();
console.log(user.data.lastName);
const product = await syncProduct();
console.log(product.data.name);
}
const fetchData = async <T>(url: string): Promise<ApiResponse<T>> => {
const response = await fetch(url);
const data = await response.json();
return { data, status: response?.status, message: response?.statusText };
};
async function main2() {
const userData = await fetchData<User>('https://www.example.com/users');
const productData = await fetchData<Product>('https://www.example.com/products');
}
What You Learned
- Core types, arrays, objects, functions
- Type inference, type aliases, unions, intersections, generics
Type each example. Break them on purpose. Read the errors. Fix them.
Part 02 covers advanced type tools next.
Top comments (0)