Bird
Raised Fist0
Typescriptprogramming~15 mins

Duck typing mental model in TypeScript - Deep Dive

Choose your learning style10 modes available
Overview - Duck typing mental model in TypeScript
What is it?
Duck typing in TypeScript means that an object's type is determined by what properties and methods it has, not by its explicit type name. If an object looks like a certain type and behaves like it, TypeScript treats it as that type. This lets you use objects flexibly without strict class inheritance or interfaces.
Why it matters
Duck typing exists to make code more flexible and easier to work with by focusing on what an object can do rather than what it is called. Without duck typing, you would need rigid class hierarchies or explicit declarations everywhere, making code harder to reuse and slower to write. It helps TypeScript catch errors while still allowing natural JavaScript-style coding.
Where it fits
Before learning duck typing, you should understand basic TypeScript types and interfaces. After this, you can explore advanced type features like type guards, structural typing, and generics to write safer and more flexible code.
Mental Model
Core Idea
If an object has the right shape—meaning the right properties and methods—TypeScript treats it as that type, regardless of its declared type name.
Think of it like...
It's like recognizing a tool by how it works, not by its label. If it quacks and walks like a duck, it's a duck, no matter what the tag says.
┌───────────────┐       ┌───────────────┐
│   Object A    │       │   Object B    │
│ ┌───────────┐ │       │ ┌───────────┐ │
│ │quack():void│ │       │ │quack():void│ │
│ │walk():void │ │       │ │walk():void │ │
│ └───────────┘ │       │ └───────────┘ │
└───────┬───────┘       └───────┬───────┘
        │                       │
        │ Has same shape        │
        └───────────────┬───────┘
                        ▼
                 Treated as same type
Build-Up - 7 Steps
1
FoundationUnderstanding TypeScript Types
🤔
Concept: Learn what types are and how TypeScript uses them to check code.
TypeScript uses types to describe what kind of data a variable holds, like number, string, or object. When you write code, TypeScript checks if you use variables correctly according to their types. For example, if a variable is a number, you can't use it like a string.
Result
You know how TypeScript uses types to catch mistakes before running code.
Understanding basic types is essential because duck typing builds on how TypeScript checks if objects fit expected shapes.
2
FoundationWhat is Structural Typing?
🤔
Concept: TypeScript checks types based on their structure, not their names.
In TypeScript, two objects are considered the same type if they have the same properties with the same types, even if they come from different places. For example, two objects with a 'name' string property are compatible, regardless of their declared types.
Result
You see that TypeScript cares about what properties an object has, not what it's called.
Knowing structural typing helps you understand why duck typing works in TypeScript.
3
IntermediateHow Duck Typing Works in TypeScript
🤔Before reading on: do you think TypeScript requires explicit inheritance to treat objects as the same type? Commit to your answer.
Concept: Duck typing means TypeScript accepts any object that matches the expected shape, without needing explicit inheritance or interface implementation.
If a function expects an object with certain properties or methods, you can pass any object that has those, even if it doesn't declare the type explicitly. For example, a function expecting {quack: () => void} accepts any object with a quack method.
Result
You can use objects flexibly, passing different shapes as long as they fit the expected structure.
Understanding this lets you write more reusable and flexible code without strict class hierarchies.
4
IntermediateInterfaces and Duck Typing Together
🤔Before reading on: do you think an object must explicitly declare an interface to be used as that interface type? Commit to your answer.
Concept: Interfaces describe expected shapes, but objects don't need to declare them to be compatible.
In TypeScript, interfaces define what properties an object should have. However, any object with matching properties can be used where that interface is expected, even if it doesn't say it implements the interface. This is duck typing in action.
Result
You can use plain objects or different classes interchangeably if they match the interface shape.
Knowing this prevents confusion about needing explicit declarations and encourages flexible design.
5
IntermediateOptional Properties and Duck Typing
🤔
Concept: Optional properties in interfaces affect compatibility in duck typing.
Interfaces can have optional properties marked with ?. When checking compatibility, objects don't need to have optional properties. For example, if an interface has an optional 'fly' method, objects without 'fly' still match the interface if they have the required parts.
Result
You understand how optional parts make duck typing more flexible.
Recognizing optional properties helps you design interfaces that accept a wider range of objects.
6
AdvancedType Compatibility and Excess Property Checks
🤔Before reading on: do you think TypeScript allows objects with extra properties beyond the expected shape? Commit to your answer.
Concept: TypeScript usually allows extra properties but warns when object literals have unexpected properties directly assigned.
When you assign an object literal to a variable or pass it as an argument, TypeScript checks for extra properties not in the expected type and may give an error. But if the object comes from a variable, extra properties are allowed. This subtlety helps catch mistakes but allows flexibility.
Result
You know when TypeScript enforces strict shape matching and when it is lenient.
Understanding this prevents confusing errors and helps you write clearer code.
7
ExpertDuck Typing Limits and Structural Type System
🤔Before reading on: do you think duck typing can detect all type errors perfectly? Commit to your answer.
Concept: Duck typing relies on structure, which can miss some semantic errors or intentional distinctions between types with the same shape.
Because TypeScript uses structural typing, two different concepts with the same shape are considered compatible. For example, types representing different units or concepts but with the same properties are interchangeable, which can cause subtle bugs. Advanced techniques like branded types or nominal typing patterns help avoid this.
Result
You realize duck typing is powerful but has limits that require careful design.
Knowing these limits helps you avoid bugs and design safer type systems in large projects.
Under the Hood
TypeScript's type system is structural, meaning it compares the shape of types by checking if required properties and methods exist with compatible types. At compile time, TypeScript verifies that objects have the expected members. It does not rely on runtime class inheritance or explicit declarations but on matching property names and types. This allows flexible assignment and function calls based on shape compatibility.
Why designed this way?
TypeScript was designed to work smoothly with JavaScript, which is dynamic and does not enforce classes or interfaces at runtime. Structural typing and duck typing allow gradual typing and easy integration with existing JavaScript code. This design avoids the rigidity of nominal typing, making TypeScript more practical and developer-friendly.
┌───────────────┐       ┌───────────────┐
│ Expected Type │       │ Actual Object │
│ ┌───────────┐ │       │ ┌───────────┐ │
│ │ propA     │ │◄──────│ │ propA     │ │
│ │ methodB() │ │◄──────│ │ methodB() │ │
│ └───────────┘ │       │ └───────────┘ │
└───────┬───────┘       └───────┬───────┘
        │                       │
        │ Structural match      │
        └───────────────┬───────┘
                        ▼
               Assignment allowed
Myth Busters - 3 Common Misconceptions
Quick: Does an object need to explicitly declare an interface to be used as that interface type? Commit to yes or no.
Common Belief:An object must explicitly declare it implements an interface to be treated as that interface.
Tap to reveal reality
Reality:TypeScript uses structural typing, so any object with the right shape matches the interface, even without explicit declaration.
Why it matters:Believing this limits flexibility and causes unnecessary code or confusion about compatibility.
Quick: Does TypeScript allow objects with extra properties beyond the expected type without any warnings? Commit to yes or no.
Common Belief:TypeScript always allows objects with extra properties beyond the expected shape without errors.
Tap to reveal reality
Reality:TypeScript warns about extra properties when assigning object literals directly but allows them when the object comes from a variable.
Why it matters:Misunderstanding this causes confusing errors or missed mistakes in object shapes.
Quick: Does duck typing guarantee that two types with the same shape are always semantically the same? Commit to yes or no.
Common Belief:If two types have the same shape, they are always interchangeable and mean the same thing.
Tap to reveal reality
Reality:Types with the same shape can represent different concepts, and duck typing treats them as compatible, which can cause subtle bugs.
Why it matters:Ignoring this can lead to mixing incompatible data and logic errors in large codebases.
Expert Zone
1
TypeScript's excess property checks only apply to object literals, not variables, which can confuse developers new to duck typing.
2
Branded or nominal typing patterns are used by experts to simulate nominal typing and avoid accidental type compatibility.
3
TypeScript's structural typing allows powerful generic programming but requires careful design to avoid unintended matches.
When NOT to use
Duck typing is not ideal when you need strict type distinctions or nominal typing, such as in financial or safety-critical systems. In those cases, use branded types, classes with private members, or runtime type checks to enforce stricter typing.
Production Patterns
In real-world TypeScript projects, duck typing enables flexible APIs, polymorphic functions, and easy integration with JavaScript libraries. Experts combine it with interfaces, type guards, and branded types to balance flexibility and safety.
Connections
Nominal Typing
Opposite typing system
Understanding duck typing's structural approach clarifies why nominal typing enforces stricter type identity, helping choose the right typing strategy.
Interface Segregation Principle (Software Design)
Builds on
Knowing duck typing helps apply interface segregation by designing small, focused interfaces that objects can easily match.
Biology - Species Identification
Similar pattern of classification
Just like biologists classify animals by observable traits rather than names, duck typing classifies objects by their shape, showing cross-domain parallels in categorization.
Common Pitfalls
#1Assuming an object must declare an interface to be compatible.
Wrong approach:interface Bird { fly(): void; } const obj = { fly: () => console.log('Flying') }; const bird: Bird = obj; // Works const anotherBird: Bird = { fly: () => console.log('Flying') }; // Error if missing explicit declaration
Correct approach:interface Bird { fly(): void; } const obj = { fly: () => console.log('Flying') }; const bird: Bird = obj; // Works const anotherBird: Bird = { fly: () => console.log('Flying') }; // Also works without explicit declaration
Root cause:Misunderstanding that TypeScript uses structural typing, not nominal typing.
#2Getting unexpected errors due to excess property checks on object literals.
Wrong approach:interface Duck { quack(): void; } const duck: Duck = { quack: () => {}, swim: () => {} }; // Error: Object literal may only specify known properties
Correct approach:interface Duck { quack(): void; } const obj = { quack: () => {}, swim: () => {} }; const duck: Duck = obj; // Works because obj is a variable
Root cause:Not knowing excess property checks only apply to object literals directly assigned.
#3Mixing types with same shape but different meanings causing bugs.
Wrong approach:type USD = { amount: number }; type EUR = { amount: number }; function payUSD(money: USD) { /* ... */ } payUSD({ amount: 10 } as EUR); // Allowed but incorrect
Correct approach:type USD = { amount: number; __brand: 'USD' }; type EUR = { amount: number; __brand: 'EUR' }; function payUSD(money: USD) { /* ... */ } payUSD({ amount: 10, __brand: 'USD' }); // Correct and safe
Root cause:Structural typing treats same-shaped types as compatible, ignoring semantic differences.
Key Takeaways
Duck typing in TypeScript means compatibility is based on an object's shape, not its declared type name.
TypeScript uses structural typing, allowing flexible and reusable code without strict inheritance.
Excess property checks help catch mistakes but only apply to object literals, not variables.
Duck typing can cause subtle bugs when different concepts share the same shape, requiring advanced patterns to avoid.
Understanding duck typing helps write safer, clearer, and more flexible TypeScript code.

Practice

(1/5)
1. What does duck typing mean in TypeScript?
easy
A. Only objects created from the same class are compatible.
B. If an object has the required properties or methods, it is accepted regardless of its explicit type.
C. TypeScript requires explicit class inheritance to match types.
D. TypeScript checks the runtime type of objects strictly.

Solution

  1. Step 1: Understand duck typing concept

    Duck typing means an object is accepted if it has the needed properties or methods, regardless of its declared type.
  2. Step 2: Compare with other options

    The other options describe strict or class-based typing, which is not how TypeScript works.
  3. Final Answer:

    If an object has the required properties or methods, it is accepted regardless of its explicit type. -> Option B
  4. Quick Check:

    Duck typing = structural compatibility [OK]
Hint: Focus on properties, not explicit types, for duck typing [OK]
Common Mistakes:
  • Thinking TypeScript requires class inheritance
  • Confusing runtime type checks with compile-time checks
  • Assuming exact type names must match
2. Which of the following is a correct TypeScript syntax to define a type that matches any object with a name string property and a quack() method returning void?
easy
A. type Duck = { name: string; quack: () => void; };
B. interface Duck { name: string; quack: void; }
C. type Duck = { name: string; quack(): string; };
D. interface Duck { name: string; quack: () => string; }

Solution

  1. Step 1: Check property and method types

    The name property must be a string, and quack must be a method returning void.
  2. Step 2: Validate each option

    type Duck = { name: string; quack: () => void; }; correctly defines quack as a function returning void. The other options have incorrect method signatures or return types.
  3. Final Answer:

    type Duck = { name: string; quack: () => void; }; -> Option A
  4. Quick Check:

    Correct method signature = type Duck = { name: string; quack: () => void; }; [OK]
Hint: Method type must be a function signature with correct return type [OK]
Common Mistakes:
  • Using method return type as property type
  • Confusing void and string return types
  • Incorrect interface method syntax
3. What will be the output of this TypeScript code?
interface Duck { name: string; quack: () => void; }

function makeItQuack(duck: Duck) {
  duck.quack();
}

const obj = { name: 'Daffy', quack: () => console.log('Quack!'), swim: () => console.log('Swim') };
makeItQuack(obj);
medium
A. No output
B. Swim
C. Error: obj does not match Duck interface
D. Quack!

Solution

  1. Step 1: Check if obj matches Duck interface

    obj has name (string) and quack (function returning void), so it matches by duck typing.
  2. Step 2: Call makeItQuack with obj

    Calling duck.quack() runs console.log('Quack!'), printing "Quack!".
  3. Final Answer:

    Quack! -> Option D
  4. Quick Check:

    Duck typing allows extra properties, output = Quack! [OK]
Hint: Extra properties don't block duck typing; focus on required ones [OK]
Common Mistakes:
  • Thinking extra properties cause errors
  • Expecting 'Swim' output instead of 'Quack!'
  • Assuming runtime type errors
4. Identify the error in this TypeScript code using duck typing:
interface Duck { name: string; quack: () => void; }

const notADuck = { name: 'Donald' };

function makeItQuack(duck: Duck) {
  duck.quack();
}

makeItQuack(notADuck);
medium
A. notADuck is missing the quack method required by Duck interface.
B. The name property should be a number, not a string.
C. makeItQuack should accept any object without type restrictions.
D. There is no error; code runs fine.

Solution

  1. Step 1: Check properties of notADuck

    notADuck has name but lacks quack method required by Duck interface.
  2. Step 2: Understand TypeScript type checking

    TypeScript will report an error because notADuck does not satisfy the Duck type.
  3. Final Answer:

    notADuck is missing the quack method required by Duck interface. -> Option A
  4. Quick Check:

    Missing required method = Type error [OK]
Hint: Check all required properties/methods exist for the type [OK]
Common Mistakes:
  • Ignoring missing methods in objects
  • Assuming extra properties cause errors
  • Thinking TypeScript allows missing required members
5. Given this code, which object will TypeScript accept as a valid Duck type?
interface Duck { name: string; quack: () => void; }

const a = { name: 'Daisy', quack: () => console.log('Quack!') };
const b = { name: 'Goose', quack: 'loud' };
const c = { name: 'Swan', quack: () => 'quack' };
const d = { name: 'Mallard' };

function testDuck(duck: Duck) { duck.quack(); }

Which call to testDuck will NOT cause a TypeScript error?
hard
A. testDuck(c);
B. testDuck(b);
C. testDuck(a);
D. testDuck(d);

Solution

  1. Step 1: Check each object's quack property type

    a.quack is a function returning void (correct). b.quack is a string (wrong). c.quack is a function returning string (wrong return type). d.quack is missing (wrong).
  2. Step 2: Determine which matches Duck interface

    Only object a matches the Duck interface exactly.
  3. Final Answer:

    testDuck(a); -> Option C
  4. Quick Check:

    Correct method signature only in a [OK]
Hint: Method must be function returning void to match Duck [OK]
Common Mistakes:
  • Accepting string or wrong return type as method
  • Ignoring missing methods
  • Confusing method return types
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard