0
0
Typescriptprogramming~15 mins

How structural typing differs from nominal typing in Typescript - Mechanics & Internals

Choose your learning style9 modes available
Overview - How structural typing differs from nominal typing
What is it?
Structural typing and nominal typing are two ways programming languages check if types match. Structural typing checks if the shape or structure of data fits what is expected, like matching puzzle pieces. Nominal typing checks if types have the same name or label, like matching ID cards. TypeScript uses structural typing, meaning it cares about what properties an object has, not what it is called.
Why it matters
Without understanding these typing styles, programmers might get confused about why some code works or fails. Structural typing allows more flexible code reuse and easier integration, while nominal typing enforces strict type identity for safety. Knowing the difference helps write better, safer, and more maintainable programs.
Where it fits
Before this, learners should know basic types and interfaces in TypeScript. After this, they can explore advanced type features like type guards, union types, and type inference.
Mental Model
Core Idea
Structural typing checks if types have the right shape, while nominal typing checks if types have the right name.
Think of it like...
Structural typing is like fitting a key into a lock based on its shape, while nominal typing is like needing the exact key with the right label or brand.
┌─────────────────────────────┐
│        Type Checking        │
├─────────────┬───────────────┤
│ Structural  │ Nominal       │
├─────────────┼───────────────┤
│ Checks shape│ Checks name   │
│ (properties)│ (type labels) │
└─────────────┴───────────────┘
Build-Up - 6 Steps
1
FoundationBasic concept of type compatibility
🤔
Concept: Types can be compatible if they share certain characteristics.
In programming, when you assign one value to another, the language checks if their types match. Compatibility means the value fits where it is used. For example, a number can be assigned to a variable expecting a number.
Result
You can assign values without errors if types are compatible.
Understanding compatibility is the base for knowing how typing systems decide if types match.
2
FoundationNominal typing explained simply
🤔
Concept: Nominal typing uses names to decide if types match.
In nominal typing, two types are compatible only if they have the same name or explicit relationship. For example, a 'User' type is different from a 'Person' type even if they have the same properties, because their names differ.
Result
Code with nominal typing rejects assignments between different named types, even if their structure is the same.
Knowing nominal typing helps understand strict type safety based on explicit type names.
3
IntermediateStructural typing in TypeScript
🤔
Concept: TypeScript uses structural typing to check compatibility based on shape.
In TypeScript, if two types have the same properties with compatible types, they are considered compatible. For example, an object with properties 'name' and 'age' matches any type expecting those properties, regardless of the type's name.
Result
You can assign objects with matching properties to variables expecting a certain type, even if their declared types differ.
Understanding structural typing explains why TypeScript allows flexible assignments that nominal typing would reject.
4
IntermediateComparing structural vs nominal typing
🤔Before reading on: do you think structural typing requires exact type names or just matching properties? Commit to your answer.
Concept: Structural typing focuses on properties, nominal typing on names.
Structural typing checks if the data has the required properties and types, ignoring the declared type names. Nominal typing requires the exact type name or explicit inheritance to allow compatibility. This difference affects how strictly types are checked and how flexible code can be.
Result
Structural typing allows more flexible code reuse; nominal typing enforces stricter type boundaries.
Knowing this difference helps predict how TypeScript behaves compared to languages with nominal typing.
5
AdvancedTypeScript's structural typing edge cases
🤔Before reading on: do you think two types with the same properties but different intentions are always interchangeable in TypeScript? Commit to your answer.
Concept: Structural typing can cause unintended compatibility if types share structure but differ in meaning.
Because TypeScript checks only structure, two types with the same properties but different purposes can be assigned to each other. For example, a 'Point2D' and a 'Color' type might both have 'x' and 'y' properties but represent different concepts. This can lead to bugs if not carefully managed.
Result
TypeScript allows assignments that may not make logical sense, requiring developers to use techniques like branding or nominal typing patterns to prevent errors.
Understanding this limitation helps write safer code by recognizing when structural typing can be too permissive.
6
ExpertSimulating nominal typing in TypeScript
🤔Before reading on: do you think TypeScript can enforce nominal typing strictly? Commit to your answer.
Concept: TypeScript can mimic nominal typing using unique symbols or branding techniques.
Though TypeScript is structurally typed, developers can create nominal types by adding unique private properties or symbols that prevent accidental compatibility. For example, adding a private field with a unique symbol makes two types incompatible even if their public properties match.
Result
This technique enforces stricter type safety and prevents unintended assignments, combining benefits of nominal typing within a structural system.
Knowing how to simulate nominal typing in TypeScript empowers developers to control type safety beyond default structural checks.
Under the Hood
TypeScript's compiler compares types by checking if all required properties and their types exist in the target type. It ignores the declared names or origins of types. This structural comparison happens during type checking and allows flexible assignments. Nominal typing, by contrast, requires explicit type identity or inheritance, which TypeScript does not enforce by default.
Why designed this way?
TypeScript chose structural typing to align with JavaScript's flexible object shapes and to enable easier integration with existing JavaScript code. Nominal typing would be too restrictive for JavaScript's dynamic nature. Structural typing allows gradual typing and smoother adoption.
┌───────────────┐       ┌───────────────┐
│   Object A    │       │   Object B    │
│ name: string  │       │ name: string  │
│ age: number   │       │ age: number   │
└──────┬────────┘       └──────┬────────┘
       │ Structure matches          │
       └─────────────┬─────────────┘
                     │
             Structural typing allows
             assignment between A and B

Nominal typing would reject this if A and B
have different declared type names.
Myth Busters - 3 Common Misconceptions
Quick: Does TypeScript require types to have the same name to be compatible? Commit to yes or no.
Common Belief:TypeScript requires types to have the same name to be compatible.
Tap to reveal reality
Reality:TypeScript uses structural typing, so compatibility depends on matching properties, not names.
Why it matters:Believing this causes confusion when code compiles despite different type names, leading to misunderstandings about type safety.
Quick: Can two types with the same properties but different meanings be safely used interchangeably in TypeScript? Commit to yes or no.
Common Belief:If two types have the same properties, they are always interchangeable safely.
Tap to reveal reality
Reality:Structural typing allows interchange, but it can cause logical errors if types represent different concepts.
Why it matters:Ignoring this can lead to bugs where data is misused because the compiler does not catch semantic mismatches.
Quick: Is it impossible to enforce nominal typing in TypeScript? Commit to yes or no.
Common Belief:TypeScript cannot enforce nominal typing at all.
Tap to reveal reality
Reality:Developers can simulate nominal typing using branding or unique symbols to prevent accidental compatibility.
Why it matters:Knowing this allows writing safer code when nominal typing is needed, avoiding subtle bugs.
Expert Zone
1
Structural typing in TypeScript also applies to function types, where parameter and return types are compared structurally, which can lead to subtle compatibility rules.
2
TypeScript's type compatibility is bidirectional for some types but not all, leading to complex rules especially with generics and union types.
3
Private and protected members affect structural compatibility, making types with different private members incompatible even if public members match.
When NOT to use
Structural typing is less suitable when strict type identity is required, such as in security-sensitive code or APIs where accidental type mixing is dangerous. In such cases, nominal typing or branding patterns should be used to enforce clear boundaries.
Production Patterns
In real-world TypeScript projects, developers often use interfaces and type aliases with structural typing for flexibility, but apply branding or unique symbols to enforce nominal typing where needed, such as for IDs or tokens. This hybrid approach balances safety and convenience.
Connections
Duck Typing
Structural typing is a form of duck typing used in static type systems.
Understanding duck typing in dynamic languages like Python helps grasp why structural typing focuses on shape rather than names.
Object-Oriented Programming (OOP) Inheritance
Nominal typing aligns with OOP inheritance where type identity matters.
Knowing OOP inheritance clarifies why nominal typing enforces strict type names and relationships.
Branding in Security Tokens
Branding techniques in TypeScript mimic nominal typing to prevent misuse of tokens.
Recognizing branding patterns in security helps understand how nominal typing concepts apply beyond programming languages.
Common Pitfalls
#1Assuming two types with the same properties are always interchangeable.
Wrong approach:type Point = { x: number; y: number }; type Color = { x: number; y: number }; let p: Point = { x: 1, y: 2 }; let c: Color = p; // Allowed but logically wrong
Correct approach:type Point = { x: number; y: number } & { __brand?: 'point' }; type Color = { x: number; y: number } & { __brand?: 'color' }; let p: Point = { x: 1, y: 2, __brand: 'point' }; let c: Color = p; // Error: incompatible types
Root cause:Structural typing ignores semantic meaning, so without branding, types with same shape are interchangeable.
#2Expecting TypeScript to prevent all type mismatches by name.
Wrong approach:interface User { name: string; } interface Person { name: string; } let user: User = { name: 'Alice' }; let person: Person = user; // Allowed, but names differ
Correct approach:Use branding or unique symbols to differentiate types if needed.
Root cause:Misunderstanding that TypeScript uses structural typing, not nominal typing.
Key Takeaways
Structural typing checks if types have matching properties, ignoring their names.
Nominal typing requires exact type names or explicit relationships for compatibility.
TypeScript uses structural typing to allow flexible and gradual typing of JavaScript code.
Structural typing can cause unintended compatibility, so branding techniques help enforce nominal typing when needed.
Understanding these typing styles helps write safer and more maintainable TypeScript code.