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
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):
class User { int id; String name; }
class Person { int id; String name; }
// User and Person are incompatible, even though the fields are identicalBut in TypeScript:
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 structureTypeScript 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:
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 number4. How TypeScript checks compatibility
TypeScript goes "by the structure" of an object:
- It looks at all the required fields of the target type.
- It checks that the passed value has the same fields with compatible types.
- It ignores extra properties (if they do not interfere with the context).
5. Example with complex structures
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 structure6. 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":
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 readyA concise answer to help you respond confidently on this topic during an interview.