Duck Typing & EAFP
Two different takes on structural typing
Section titled “Two different takes on structural typing”TypeScript and Python are both structurally typed — a value is compatible with a type if it has the right shape. But the mechanism is completely different.
TypeScript verifies structure at compile time using interfaces and type aliases. If a value does not satisfy the interface, the compiler rejects it before the code runs. Python checks structure at runtime by simply trying to call the method or access the attribute. If the object has it, great. If not, you get an AttributeError.
This runtime approach is called duck typing: if it walks like a duck and quacks like a duck, it is a duck. Python does not care about declared types — only about whether the object can do what you ask of it.
Duck typing: interfaces vs behavior
Section titled “Duck typing: interfaces vs behavior”// TypeScript: explicit interface requiredinterface Speaker { speak(): string;}
class Dog implements Speaker { speak(): string { return "Woof!"; }}
class Cat implements Speaker { speak(): string { return "Meow!"; }}
// TypeScript verifies the interface at compile timefunction makeSound(animal: Speaker): void { console.log(animal.speak());}
makeSound(new Dog());makeSound(new Cat());# Python: no interface needed — just call the methodclass Dog: def speak(self) -> str: return "Woof!"
class Cat: def speak(self) -> str: return "Meow!"
class Duck: def speak(self) -> str: return "Quack!"
# Python checks at runtime whether .speak() existsdef make_sound(animal) -> None: print(animal.speak())
make_sound(Dog())make_sound(Cat())make_sound(Duck()) # works — Duck was never declared as SpeakerIn TypeScript, Duck would need to explicitly implement Speaker. In Python, any object with a .speak() method works — no declaration required.
LBYL vs EAFP
Section titled “LBYL vs EAFP”The structural difference extends to error handling. TypeScript developers tend to use Look Before You Leap (LBYL): check whether an operation is safe before attempting it. Python culture favors Easier to Ask Forgiveness than Permission (EAFP): just try it, and handle the exception if it fails.
// TypeScript: LBYL — check first, then actfunction getValue(obj: Record<string, unknown>, key: string): unknown { if (key in obj) { return obj[key]; } return undefined;}
// Also common in TypeScriptfunction parseNumber(s: string): number | null { const n = Number(s); return isNaN(n) ? null : n;}# Python: EAFP — try it and handle failuredef get_value(d: dict, key: str): try: return d[key] except KeyError: return None
# LBYL equivalent works too, but is less idiomaticdef get_value_lbyl(d: dict, key: str): if key in d: return d[key] return None
# EAFP for type conversiondef parse_number(s: str): try: return int(s) except ValueError: return NoneTry it
Section titled “Try it”class Dog: def speak(self): return "Woof!"
class Cat: def speak(self): return "Meow!"
class Duck: def speak(self): return "Quack!"
def make_sound(animal): print(animal.speak())
for animal in [Dog(), Cat(), Duck()]: make_sound(animal)
# EAFP patterndef get_value(d, key): try: return d[key] except KeyError: return "not found"
data = {"name": "Alice", "age": 30}print(get_value(data, "name"))print(get_value(data, "email"))Loading Python runtime (first run only)…