TypeScript Interfaces and Type Aliases - Questions & Answers

1. What are interfaces in TypeScript?

Interfaces: A TypeScript construct for defining the shape of an object, specifying properties, their types, and optionally methods.

Can you give an example of defining interfaces?


// Defining interfaces
interface User { id: number; name: string; email: string; greet(): string;
} // Using interface
const user: User = { id: 1, name: "kristal", email: "[email protected]", greet() { return `Hello, ${this.name}!`; }
}; console.log(user.greet()); 

Output:Hello, kristal!

2. What are optional and readonly properties in TypeScript interfaces?

Optional Properties: Properties that may or may not be present, denoted by ?.

Readonly Properties: Properties that can only be set during initialization, denoted by readonly.

Can you give an example of optional and readonly properties?


// Optional and readonly properties
interface Product { readonly id: number; name: string; description?: string; // Optional price: number;
} const product: Product = { id: 101, name: "Laptop", price: 999.99 // description omitted (optional)
}; console.log(product);
// product.id = 102; // Error: Cannot assign to 'id' because it is a read-only property
product.price = 1099.99; // Allowed
product.description = "High-performance laptop"; // Allowed
console.log(product); 

Output:


{ id: 101, name: 'Laptop', price: 999.99 }
{ id: 101, name: 'Laptop', price: 1099.99, description: 'High-performance laptop' } 

3. What are type aliases, and how do they differ from interfaces in TypeScript?

Type Aliases: Define a type with a name, using the type keyword.

Interfaces: Specifically define object shapes, using the interface keyword.

Key Differences:

FeatureInterfaceType Alias
Declarationinterface Name { ... }type Name = { ... }
ExtendingUse extends for inheritanceUse & for intersection
MergingSupports declaration mergingNo merging; redefinition causes error
Type FlexibilityPrimarily for object shapesAny type (unions, primitives, etc.)
Use CaseObject-oriented design, reusable contractsFlexible types, including non-objects
Implement in ClassesCan be implemented (implements)Cannot be directly implemented

Use Case:

Can you give an example illustrating differences between interfaces and type aliases?


// Interface vs Type Alias
// Interface
interface User { id: number; name: string;
} // Interface merging
interface User { email: string;
} // Type Alias
type Person = { id: number; name: string;
} & { age: number }; // Intersection // Type Alias for union types
type ID = number | string; // Using interface
const user: User = { id: 1, name: "kristal", email: "[email protected]"
}; // Using type alias
const person: Person = { id: 2, name: "Sashi", age: 30
}; const id: ID = "ID123"; // Can be number or string console.log(user);
console.log(person);
console.log(id); // Class implementing interface
class Employee implements User { id: number; name: string; email: string; constructor(id: number, name: string, email: string) { this.id = id; this.name = name; this.email = email; }
} // Type alias cannot be implemented directly
// class Invalid implements Person {} // Error 

Output:


{ id: 1, name: 'kristal', email: '[email protected]' }
{ id: 2, name: 'Sashi', age: 30 }
ID123 

4. Can you provide a comprehensive example of interfaces, optional/readonly properties, extending interfaces, and type aliases?

Project Structure:


ts-interfaces-types/
├── src/
│ └── main.ts
├── tsconfig.json
└── package.json 

main.ts:


// Comprehensive example
// Interface with optional and readonly properties
interface Person { readonly id: number; name: string; email?: string; // Optional greet(): string;
} // Extending interface
interface Employee extends Person { role: string; department?: string; // Optional
} // Type alias for union
type Role = "Developer" | "Manager" | "Admin"; // Type alias with intersection
type Manager = Employee & { teamSize: number;
}; // Class implementing interface
class Staff implements Employee { readonly id: number; name: string; email?: string; role: Role; department?: string; constructor(id: number, name: string, role: Role, email?: string, department?: string) { this.id = id; this.name = name; this.role = role; this.email = email; this.department = department; } greet(): string { return `Hello, ${this.name} (${this.role})`; }
} // Using type alias and interface
const manager: Manager = { id: 1, name: "kristal", role: "Manager", email: "[email protected]", department: "Engineering", teamSize: 10, greet() { return `Hello, ${this.name}, managing ${this.teamSize} people`; }
}; const staff: Employee = new Staff(2, "Sashi", "Developer", "[email protected]"); // Testing
console.log(manager.greet());
console.log(staff.greet());
// manager.id = 3; // Error: Cannot assign to 'id' because it is a read-only property
console.log(manager);
console.log(staff); 

package.json:


{ "name": "ts-interfaces-types", "version": "1.0.0", "scripts": { "start": "tsc && node dist/main.js", "build": "tsc", "watch": "tsc --watch" }, "devDependencies": { "typescript": "^5.6.2" }
} 

tsconfig.json:


{ "compilerOptions": { "target": "ES2020", "module": "NodeNext", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true }, "include": ["src/**/*"], "exclude": ["node_modules"]
} 

Steps to Run:

  1. Create the project structure.
  2. Install dependencies: npm install.
  3. Compile: npm run build.
  4. Run: npm start.

Output:


Hello, kristal, managing 10 people
Hello, Sashi (Developer)
{ id: 1, name: 'kristal', role: 'Manager', email: '[email protected]', department: 'Engineering', teamSize: 10, greet: [Function: greet] }
Staff { id: 2, name: 'Sashi', role: 'Developer', email: '[email protected]', department: undefined } 

Description:

5. What are common mistakes in TypeScript interfaces and type aliases?

6. What are best practices for TypeScript interfaces and type aliases?

  1. Defining Interfaces:
    • Use clear, descriptive names for interfaces (e.g., User vs. IUser).
    • Define all required properties explicitly; use optional properties sparingly.
  2. Optional/Readonly Properties:
    • Use readonly for immutable fields like IDs or constants.
    • Use optional properties (?) for fields that may be absent.
  3. Extending Interfaces:
    • Extend interfaces to promote code reuse and modularity.
    • Ensure extended interfaces align with the base contract.
  4. Interfaces vs. Type Aliases:
    • Use interfaces for object shapes, especially with merging or class implementation.
    • Use type aliases for unions, intersections, or non-object types.
  5. General:
    • Follow TypeScript naming conventions: CamelCase for interfaces/types.
    • Use tsc to catch errors early.
    • Document interfaces/types with comments for clarity.
    • Test with edge cases (e.g., missing optional properties, invalid types).
    • Use tools like tslint or eslint with TypeScript plugins for consistency.