TypeScript: Understanding structural typing
Duck and structural typing
When we talk about duck typing or structural typing we are talking about the compatibility that different types may or may not have in a given programming language.
If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.
Following that precept, it means that rather than try to determine whether an object can be used for a purpose according to its type, in duck typing, an object’s suitability is determined by the presence of certain properties.
TypeScript models this behavior and bases its types compatibility on structural typing, in contrast to nominal typing.
Structural typing makes TypeScript not as strict as nominally typed languages like C# or Java and this sometimes can lead to confusion or unexpected results.
Having a good understanding of structural typing can help you make sense of errors and non-errors and help you write more robust code.
Understanding structural typing
The basic rule for TypeScript’s structural type system is that type
A is compatible with type
B has at least the same members as
Say we have 2 different interfaces:
Structural typing allows to assign an object of type
Person but not the other way around because
Person lacks of the property
This might look simple to deal with but it can sometimes lead to confusing results.
Say you want to create a function that prints the details of
You might think that something like the following would do the job
person[property] will produce the following error:
Element implicitly has an ‘any’ type because expression of type ‘string’ can’t be used to index type ‘Person’.
The logic in the previous example assumes that the
Person passed to the function is a “sealed” (or “precise”) type and therefore every property of the object would exist.
But as we saw earlier that is not the case. Like it or not types in TypeScript are “open” and they might contain more properties than the ones declared.
According to structural typing I could pass an
Alien and the type checker wouldn’t complain about it. The problem is when we iterate over the properties of
Person and because it might have to deal with unknown properties like
planet, the type checker gives us that error.
If you are after a solution to this problem have a look to this article: How to iterate over object properties.
Benefits of structural typing
Structural typing is not always bad and in some circumstances it can be beneficial, for instance when you are writing tests.
Say you have a function that returns the users of your application and process the results.
In order to test
getUsers you could mock
Repository but a better approach would be leveraging structural typing and create a narrower interface:
You can still pass
Repository since it has a
findUsers method and because of structural typing it doesn’t need to implement
But in your test you can now pass a simpler object: