Introduction
In this article, we understand the core TypeScript types, let's see we have primitives then objects then arrays.
Primitive Types
Primitive types represent single values like strings, numbers, and booleans.
// A hero's name must be a string
let heroName: string = "Iron Man";
// Power levels are numeric: clearly very serious business
let powerLevel: number = 9000;
// Booleans handle yes/no questions, like "Is this guy an Avenger?"
let isAvenger: boolean = true;
// Null represents intentional absence
let sidekick: null = null;
// Undefined is the default absence
let identity: undefined = undefined;
// BigInt handles extremely large numbers (like Tony's net worth)
let billionDollars: bigint = 1_000_000_000_000n;
Be explicit with types when it helps code clarity.
Union Types
Union types let a variable accept multiple possible types. Ideal when dealing with flexible input or multi-state outputs.
// Status can be a string *or* a number: depending on the situation
let status: string | number;
status = "loading..."; // loading as text
status = 200; // or as HTTP-style code
Function with a union type parameter
The printStatus function accepts a parameter code that can be either a number or a string, and it logs a message based on the type of code. The return type is void, meaning it doesn't return anything.
// Function handling both string and number status
function printStatus(code: number | string): void {
if (typeof code === "number") {
console.log(`Status code: ${code}`);
} else {
console.log(`Status message: ${code}`);
}
}
Use typeof to deal with different types safely in union scenarios.
Let's understand what in the world Type
A type is like a contract or blueprint that a variable or object must follow.
In TypeScript, types tell the compiler
- What kind of value a variable holds (e.g., number, string, object, array, etc.)
- What operations are allowed on that value
- What properties or methods must or may exist
Everything we saw above are types in primitive types is a Type.
- string: for text ("Iron Man")
- number: for numeric values (9000)
- boolean: for true/false (true)
- null: for intentional emptiness (null)
- undefined: for uninitialized or missing values (undefined)
- bigint: for very large integers (1_000_000_000_000n)
All of these are types that describe what kind of data a variable can hold.
then there's customer type,
Object Types
We all know object types describe complex structures with multiple properties, great for modeling real-world entities.
// Defining a Hero type with optional 'isAvenger' property
type Hero = {
name: string;
powerLevel: number;
isAvenger?: boolean; // Optional property
};
// Thor fits the Hero type
const thor: Hero = {
name: "Thor",
powerLevel: 9500,
};
1. When to use: vs =
- Use: when declaring the type of a property or variable.
- Example: name: string, this means the name must be a string.
- Use = when assigning a value to a variable or object property.
- Example: name = "Thor" (inside an object literal).
2. What does? mean in isAvenger?: boolean?
- The ? makes the property optional, meaning it’s not required when creating an object of that type.
- So in the thor object, we can omit isAvenger, and TypeScript won’t throw an error.
// A more advanced Hero with a method and readonly ID
type AdvancedHero = {
readonly id: number;
name: string;
attack(): void;
};
const captain: AdvancedHero = {
id: 1,
name: "Captain America",
attack() {
console.log("Shield!");
},
};
// captain.id = 2; ❌ Error: Cannot assign to 'id' because it is a read-only property
1. readonly
- readonly id: number: This means the id cannot be changed after it's set. It's read-only.
- attack(): void: A method inside the object. It doesn’t return anything (void) but performs an action (logs "Shield!").
If this is what readonly then how is it different from const?
readonly vs. const
readonly is for object properties
- Used within types or interfaces to prevent modification of specific properties.
- The object can be reassigned, but the readonly properties cannot be changed.
type Hero = {
readonly name: string;
};
const h: Hero = { name: "Thor" };
h.name = "Loki"; // ❌ Error: Cannot assign to 'name' because it is a read-only property
But you can reassign the whole object if it's not declared with const.
let h: Hero = { name: "Thor" };
h = { name: "Loki" }; // new object entirely
Interfaces
An interface is a way to define the structure of an object. it describes.
- What properties the object should have
- What types those properties should be
- What methods (functions) the object must implement
It acts like a contract that the object must follow.
interface Avenger {
name: string;
weapon: string;
assemble(): void;
}
Implementing the Interface
const captain: Avenger = {
name: "Captain America",
weapon: "Shield",
assemble() {
console.log("Avengers, assemble!");
},
};
- Captain matches the Avenger interface perfectly.
- If you miss a property or use the wrong type, TypeScript will give an error.
const wrongAvenger: Avenger = {
name: "Thor",
weapon: "Hammer",
// assemble method is missing ❌
};
Interface vs Type
Both interface and type can describe objects, but
- interface is better for extending, and object-like structures
- type is more flexible (can combine primitives, unions, tuples, etc.)
Array Types
Arrays let you handle lists of items. TypeScript ensures all items in the array match the declared type.
// Array of hero names
let avengers: string[] = ["Iron Man", "Hulk", "Thor"];
// Array of numeric power levels
let powerLevels: Array<number> = [9000, 8500, 9500];
// Or
let powerLevels: number[] = [9000, 8500, 9500]; //
Array with object type
// Array of Villain objects
type Villain = {
name: string;
evilPlan: string;
};
let villains: Villain[] = [
{ name: "Loki", evilPlan: "Rule Asgard" },
{ name: "Thanos", evilPlan: "Snap away half the universe" },
];
Tuple
A tuple is a special type of array where,
- The number of elements is fixed
- The types and order of the elements are specified
Think of it like a typed, structured mini-array.
let heroTuple: [string, number] = ["Iron Man", 9000];
The first element must be a string and the second element must be a number.
let heroTuple : [string, number] = ["Iron Man", 9000];
heroTuple[0] = "Iron Man";
heroTuple[1] = 9000;
heroTuple[0] = 9000; // ❌ Error: Type 'number' is not assignable to type 'string'.ts
heroTuple[1] = "Iron Man"; // ❌ Error: Type 'string' is not assignable to type 'number'.ts
Common Use Case
Tuples are great for returning multiple values from a function.
function getHeroInfo(): [string, number] {
return ["Captain America", 8700];
}
const [name, power] = getHeroInfo();
Tuples can be misused as normal arrays if you're not strict. TypeScript allows .push() on tuples, though it's generally discouraged.
heroTuple.push("extra"); // Allowed, but not type-safe in logic
Bonus
- null vs. undefined: Both mean nothing, but they’re very different flavors of nothing.
- undefined: "I wasn't assigned anything: undefined is the default state of a variable that hasn’t been initialized.
let x: number | undefined;
console.log(x); // undefined
It's what JavaScript returns when,
- You access a missing property.
- You don’t return anything from a function.
- You forget to initialize a variable.
function doSomething(): void {
// No return
}
const result = doSomething(); // result is undefined
Null: I was assigned nothing on purpose.
null is intentional. It’s a value you assign when you mean nothing here but on purpose.
let sidekick : string | null = null;
This is useful when you want to explicitly clear a value or say “This is empty, and that’s OK.”
type Hero = {
name: string;
sidekick?: string | null; // Could be missing or deliberately set to null
};
const batman: Hero = {
name: "Batman",
sidekick: null, // He *had* a sidekick, but now he's flying solo
};
Use undefined when a value might not be present. Use null when a value is deliberately empty or cleared. Avoid mixing them unless you're modeling legacy data or a complex API.
2. any
The any type in TypeScript tells the compiler: “Trust me, I know what I’m doing.”
When a variable is typed as any, TypeScript disables all type-checking for it. You can assign it anything, call any method on it, and you won’t get any compiler errors.
let mystery: any = "Hey, now I am a string";
mystery = 1234; //now I am a number
So when should you use any?
Only when you absolutely have to, like,
- Migrating JavaScript code to TypeScript gradually
- Dealing with legacy code where typing isn’t feasible yet
- Working with third-party libraries that don’t have types
3. Use unknown instead of any
unknown is safer because it forces you to narrow the type before using it.
let value: unknown = "Hey, now I am string";
value.toUpperCase();//'value' is of type 'unknown'.ts
if(typeof value === "string"){
console.log(value.toUpperCase());
}
Conclusion
By using TypeScript's type system, you are in a better position to prevent bugs before they happen, it also makes your code more readable and self-documenting.
From primitive types to unions, objects, and arrays, you’ve just leveled up your TypeScript fundamentals.