JavaScript Core/
Lesson

You've seen basic types. Now let's define your own complex types. TypeScript gives you two main tools: interfaces and type aliases. Think of interfaces as contracts, formal agreements about what an object must contain. Type aliases are more like nicknames, they can describe anything from simple primitives to complex type transformations.

Defining object shapes with interfaces

Interfaces are the most common way to define what an object should look like.

interface User {
  id: number;
  name: string;
  email: string;
  age?: number;              // Optional property
  readonly createdAt: Date;  // Can't be modified after creation
}

const user: User = {
  id: 1,
  name: 'John Doe',
  email: 'john@example.com',
  createdAt: new Date()
};

Extending interfaces

Interfaces can extend each other, creating hierarchies:

interface Animal {
  name: string;
  age: number;
}

interface Dog extends Animal {
  breed: string;
  bark(): void;
}

const myDog: Dog = {
  name: 'Rex',
  age: 3,
  breed: 'Golden Retriever',
  bark: () => console.log('Woof!')
};
02

Type aliases: The flexible alternative

Type aliases use the type keyword and can represent much more than just object shapes.

// Object shape (similar to interface)
type Point = { x: number; y: number };

// Union type - value can be one of several types
type Status = 'pending' | 'active' | 'inactive';

// Tuple type
type Coordinates = [number, number];

// Function type
type Callback = (error: Error | null, data: string) => void;

Intersection types

While interfaces use extends, type aliases use & to combine types:

type Person = { name: string; age: number };
type Employee = { employeeId: number; department: string };

type EmployeePerson = Person & Employee;
// Has all properties of both types
03

Interface vs type aliasWhat is type alias?A name given to any TypeScript type using the type keyword, including unions, objects, and complex shapes.

FeatureInterfaceType Alias
Object shapesPrimary use caseWorks fine
Extendingextends keywordVia intersection &
Declaration mergingSupportedNot supported
Union typesNot possiblePrimary use case
Primitives & tuplesNot possibleSupported
04

Utility types: Transform existing types

TypeScript provides built-in utility types that create variations of a base type.

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
}

type PartialUser = Partial<User>;          // All properties optional
type UserSummary = Pick<User, 'id' | 'name'>;  // Only id and name
type PublicUser = Omit<User, 'password'>;   // Everything except password
type ReadonlyUser = Readonly<User>;         // All properties readonly
UtilityWhat it doesExample
Partial<T>All properties optionalPartial<User>
Required<T>All properties requiredRequired<Config>
Pick<T, K>Keep only specified keysPick<User, 'id' \| 'name'>
Omit<T, K>Remove specified keysOmit<User, 'password'>
Readonly<T>All properties readonlyReadonly<Config>
Record<K, V>Object with keys K and values VRecord<string, number>
05

Quick reference

ConceptUse interfaceUse type alias
Object shapes for APIsYes, supports extension and mergingFine, but no merging
Union types (A \| B)Not possibleYes, only way
Tuple typesNot possibleYes, only way
Composing from piecesextends keyword& intersection
Transforming typesVia utility typesVia utility types
AI pitfall, everything optional. AI tools love marking properties as optional (?) to avoid type errors. If ChatGPT generates an interface where every single field is optional, your type provides almost no safety, any empty object {} satisfies it. Review each property and ask: "Can this legitimately be missing?" Required properties should stay required.