Writing annotations for primitive values and object literals is pretty straightforward, but it differs slightly when it comes to object destructuration. In this post, I want to shortly mention differences and give a couple of examples of how to write annotations for the destructured properties of object literals.
Object literal
For simple object literals we can write annotations just after the variable and before the object. For example:
const car: { model: string; year: number } = {
model: "Ford",
year: "1969",
};
In short, we write the whole object structure and assign types before the actual object.
If the object has nested properties, we list them in the similar structure:
const car: { model: string; year: number; owner: { name: string } } = {
model: 'Ford Mustang',
year: 1969,
owner: {
name: 'John',
},
}
Object Destructuring
Giving types for destructured object properties is not very common. Usually, the inference system is reliable and assigns correct types for variables accurately, well most of the time. But there are cases when you need to add types manually. For example, if a function returns an object and it has any
type, or if the object is initiated first and some properties are added later.
We will use a declared object bellow as an example. It has a couple of properties, including a nested object and a method. We will destructure all properties using ES6 syntax and explain how to add annotations to them.
const car = {
model: 'Ford Mustang',
year: 1969,
owner: {
name: 'John',
},
setOwner(name: string): void {
this.owner.name = name;
}
}
Lets take out the first property from the object:
const { model } = car;
First, we cannot add just a type for a variable. Meaning, that writing const { model }: string = car
is incorrect. What's wrong with this approach? Well, imagine if you have to pull out a second property from the same object: const { mode, year }: string = car
then which is the string?
When destructing, a type system expects you to write the full object property, which gives the ability to add types for all the properties:
const { model }: { model: string } = car;
// pull out more properties
const { model, year }: { model: string; year: number } = car;
The same goes for the nested properties:
// destructure name property
const { owner: { name } } = car;
// mirror the whole "owner" property
const { owner: { name } }: { owner: { name: string } } = car;
The methods will have similar syntax, though they need to mention that preferable methods should be annotated inside the object, and TypeScript will notify you. Nevertheless, let's add type for a destructured method:
const { setOwner }: { setOwner: (name: string) => void } = car;
Conclusion
To sum up, if annotations are required for destructured object properties, we have to mirror and write the exact structure of that property.
Top comments (1)
Thanks for the post :)
And even though it's probably obvious, just in case someone was wondering (as I was), if you want to rename the variable, you have to type the original name in the object literal
const { model: carModel }: { model: string } = car;