Skip to main content
Practice Problems

Structural typing (duck typing) in TypeScript

What is Structural Typing?

TypeScript uses a structural type system β€” types are compatible based on their structure (shape/properties), not their name or declaration. If two types have the same structure, they are compatible.

"If it walks like a duck and quacks like a duck, it must be a duck."


Basic Example

typescript
interface Point { x: number; y: number; } // No explicit "implements Point" needed! const point = { x: 10, y: 20, z: 30 }; function printPoint(p: Point): void { console.log(`(${p.x}, ${p.y})`); } printPoint(point); // βœ… Works! point has x and y

Structural vs Nominal Typing

Structural (TypeScript)Nominal (Java, C#)
Types match by structureTypes match by name/declaration
No implements neededMust explicitly implement interface
More flexibleMore strict
typescript
// In TypeScript (structural): interface Cat { meow(): void } interface CatLike { meow(): void } const cat: Cat = { meow() {} }; const catLike: CatLike = cat; // βœ… Same structure = compatible // In Java (nominal): this would FAIL // Cat and CatLike are different types even with same methods

Excess Property Checking

TypeScript has special strict checking for object literals (direct assignment):

typescript
interface Config { host: string; port: number; } // ❌ Excess property check β€” object literal const config: Config = { host: "localhost", port: 3000, debug: true // ❌ Error: 'debug' does not exist in type 'Config' }; // βœ… No excess check β€” through variable const obj = { host: "localhost", port: 3000, debug: true }; const config: Config = obj; // βœ… Works (structural compatibility) // βœ… No excess check β€” through function argument function startServer(config: Config) {} startServer(obj); // βœ… Works

Class Compatibility

typescript
class Animal { name: string; constructor(name: string) { this.name = name; } } class Person { name: string; constructor(name: string) { this.name = name; } } // Structurally compatible β€” same shape! let animal: Animal = new Person("Alice"); // βœ… let person: Person = new Animal("Dog"); // βœ…

When Structural Typing Causes Issues

typescript
type USD = number; type EUR = number; // ❌ No type safety β€” both are just numbers function convertToEUR(amount: USD): EUR { return amount * 0.85; } const euros: EUR = 100; convertToEUR(euros); // βœ… No error! (but logically wrong)

Solution: Branded Types

typescript
type USD = number & { __brand: "USD" }; type EUR = number & { __brand: "EUR" }; function usd(amount: number): USD { return amount as USD; } function convertToEUR(amount: USD): EUR { return (amount * 0.85) as EUR; } const dollars = usd(100); convertToEUR(dollars); // βœ… convertToEUR(100 as EUR); // ❌ Error: EUR is not assignable to USD

Important:

Structural typing makes TypeScript flexible and compatible with JavaScript patterns. However, it means types with the same shape are interchangeable β€” use branded types when you need nominal-like behavior (e.g., currency, user IDs, etc.).

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems