TypeScript is a natural extension of JavaScript that’s used in many projects in place of JavaScript.
However, not everyone knows how it actually works.
In this article, we’ll look at how to work with objects in TypeScript.
Shape Types
We can define shape types that restrict the types of an object.
For instance, we can write:
const obj: { foo: number; bar?: sting } = { foo: 1 };
We have an object type with the property foo
that is a number and the property bar
that is optional and has the data type string.
The ?
after the name indicates that it’s optional.
The structure must match what’s set for the object to be assigned to the variable.
We can also include methods in object shape types.
For instance, we can write:
const obj: { name: string; greet(greeting: string): string } = {
name: "james",
greet(greeting) {
return `${greeting} ${this.name}`;
}
};
Now we have a greet
method in the object shape type, which is required.
It takes a greet
parameter with the data type string and also returns a string.
Like value properties, methods can also be optional.
For instance, we can write:
const obj: { name: string; greet?(greeting: string): string } = {
name: "james"
};
Then we can omit the greet
method in the object we assign to obj
if we wish.
Strict Checking for Methods
We can set the strictNullChecks
option to true
in tsconfig.json
to prevent undefined
values from being set in shape types.
With it on, we can write something like:
const obj: { name: string; greet?(greeting: string): string } = {
name: undefined
};
We’ll get the error ‘Type ‘undefined’ is not assignable to type ‘string’.ts(2322)‘.
Type Aliases for Shape Types
Since writing shape types are such a pain, we can assign it to a type alias so that we can use the alias instead of specifying the same type everywhere.
For instance, we can write:
type person = { name: string; greet?(greeting: string): string };
Now we can rewrite our assignment statement as follows:
const obj: person = {
name: "jame"
};
Excess Properties
The TypeScript compiler is good at inferring types, which means that data types can be skipped sometimes.
For instance, if we have:
type person = { name: string };
const obj = {
name: "jame",
age: 10
};
const james: person = obj;
It’s smart enough to match the shape of obj
and the person
type and finger that obj
is of type person
.
Shape Type Unions
Shape types can form a union type with other shape types.
For instance, we can write:
type Person = { name: string };
type Location = { city: string };
const obj = {
name: "jame",
city: "new york"
};
const james: Person | Location = obj;
Person | Location
is a data type that has both the properties of name
and city
.
Union Property Types
Properties can also have union types as their data type.
For instance, we can write:
type Person = { id: number | string; name: string };
const obj = {
name: "jame",
id: 1
};
Now id
can be a number or a string.
Type Guards for Objects
We can add type guards for objects just like we do with other types.
For instance, we can write:
type Person = { name: string };
type Animal = { breed: string; name: string };
const person = {
name: "jame"
};
const animal = {
name: "jame",
breed: "dog"
};
const arr: (Person | Animal)[] = [person, animal];
arr.forEach(a => {
console.log(typeof a);
});
We loop through the person
and animal
objects which are put into the arr
array.
Then we loop through them with forEach
to find out the type.
Inside the callback, we use the typeof
keyword to look at the type and see that they’re both type object
just like they are in JavaScript.
Checking Properties
This means that we can’t use the typeof
operator to check the type of objects.
Instead, we must find better alternatives.
One way is to check if a property is in the object.
For instance, we can use the in
operator:
arr.forEach(a => {
if ("breed" in a) {
console.log("animal");
} else {
console.log("person");
}
});
The in
operator checks if the 'breed'
property is in an object.
It checks both its own and inherited properties.
And it returns true
if a property is found in any of those places and false
otherwise.
However, this doesn’t help us if a property is in both types.
Conclusion
We can define object shape types.
This way, we can check the structure of the object and the property types.
We can assign them to a data type alias to make our lives easier by avoiding repetition.