Skip to main content

What does "structural typing" mean?

1. Essence of structural typing

Structural typing means that two types are considered compatible if they have the same structure (that is, the same set of properties and their types), even if they were declared differently.

In other words: TypeScript looks not at the name of a type, but at its shape.


Example

javascript
interface User { id: number; name: string; } const person = { id: 1, name: "Tim", age: 25 }; function printUser(user: User) { console.log(user.name); } printUser(person); // correct!

Although person is not declared as type User, TypeScript accepts it, because it is compatible by structure (it has all the needed fields: id and name).

TypeScript does not check how an object was created, it checks whether it has the needed properties of the needed types.


2. The opposite - "nominal typing"

In languages with nominal typing (for example, Java, C#, Swift), two types are considered compatible only if they are declared as related (for example, through inheritance or a shared name).

Example (Java-style):

javascript
class User { int id; String name; } class Person { int id; String name; } // User and Person are incompatible, even though the fields are identical

But in TypeScript:

javascript
interface User { id: number; name: string } interface Person { id: number; name: string } let user: User = { id: 1, name: "Tim" }; let person: Person = user; // OK - same structure

TypeScript does not care that these are "different interfaces", only the shape of the object matters.


3. Example of incompatibility

If at least one property is missing or a type differs, the types are incompatible:

javascript
interface User { id: number; name: string; } const invalid = { id: "1", name: "Tim" }; // id is not a number let u: User = invalid; // Error: id must be number

4. How TypeScript checks compatibility

TypeScript goes "by the structure" of an object:

  1. It looks at all the required fields of the target type.
  2. It checks that the passed value has the same fields with compatible types.
  3. It ignores extra properties (if they do not interfere with the context).

5. Example with complex structures

javascript
interface Point { x: number; y: number; } interface Coordinates { x: number; y: number; } const draw = (p: Point) => console.log(p.x, p.y); const c: Coordinates = { x: 10, y: 20 }; draw(c); // Compatible - same structure

6. Why this is useful

Structural typing makes code:

  • more flexible, no need to explicitly inherit types;
  • easier to test and substitute objects;
  • easier for working with external APIs, where structure matters more than the name.

7. But there is also a risk

Sometimes structural typing can lead to "false compatibility":

javascript
interface Cat { name: string; meow(): void; } interface Dog { name: string; bark(): void; } const dog: Dog = { name: "Rex", bark: () => {} }; // @ts-ignore const cat: Cat = dog; // There will be an error, but the logic looks similar cat.meow(); // runtime error!

TypeScript only checks that the method names match, not their behavior, and that is the limitation of structural typing.


Summary

Structural typing is the principle by which TypeScript determines type compatibility by shape, not by name.

Compatible - if the fields and types match. Incompatible - if something is missing or a type does not match.

This makes TypeScript flexible, expressive and convenient for the JavaScript ecosystem, where the structure of objects matters more than their origin.

Short Answer

Interview ready
Premium

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