- Interface: Primarily used to define the structure of objects or classes.
- Type alias: Can define any type, including primitives, unions, tuples, etc.
interface Person { name: string; age: number }
type ID = number | string;
- Interface: Supports declaration merging.
- Type alias: Cannot be merged.
interface Person {
name: string;
}
interface Person {
age: number;
}
// Merged: { name: string; age: number }
type ID = number;
// type ID = string; // ❌ Error
- Interface: Use
extends
to extend interfaces. - Type: Use
&
(intersection) to combine types.
interface A { a: string }
interface B extends A { b: number }
type X = { x: string }
type Y = X & { y: number }
- Interface: Can be implemented by classes using
implements
. - Type alias: Cannot be implemented directly in a class.
interface Shape {
area(): number;
}
class Circle implements Shape {
constructor(private radius: number) {}
area() { return Math.PI * this.radius ** 2; }
}
- Interface:
- Best for object shapes and class contracts.
- Preferred for public API definitions due to merging and extendability.
- Type alias:
- Best for complex types: unions, tuples, primitives, mapped types.
- Flexible for combining multiple types.
- Interface: Must describe an object shape.
interface User { name: string; age: number }
- Type alias: Can describe any type.
type User = { name: string; age: number }
type ID = string | number
type Tuple = [number, string]
The keyof
keyword in TypeScript is used to create a union type of the keys of an object type. It allows you to refer to the property names of a type in a type-safe manner.
keyof
returns a union of string literal types representing the keys of the object.- Often used with generic types to create flexible, type-safe functions.
interface Person {
name: string;
age: number;
city: string;
}
type PersonKeys = keyof Person;
// Equivalent to: "name" | "age" | "city"
let key: PersonKeys;
key = "name"; // ✅ valid
key = "age"; // ✅ valid
// key = "country"; // ❌ Error: Type '"country"' is not assignable to type 'PersonKeys'
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person: Person = { name: "Alice", age: 30, city: "Sydney" };
const personName = getProperty(person, "name"); // type: string, value: "Alice"
const personAge = getProperty(person, "age"); // type: number, value: 30
// const invalid = getProperty(person, "country"); // ❌ Error
K extends keyof T
ensures that the key passed exists in the object typeT
.T[K]
gets the type of the property corresponding to the key.- This makes functions type-safe, preventing invalid property access at compile time.