There are numerous methods for improving your code’s readability, maintainability, and extensibility. Clean Code principles must be followed, as well as various architectures, letter casing rules, and so on. Using types is still, in my opinion, one of the best ways to write great code.
I’m pretty sure you’re already familiar with TypeScript’s basic types like number
:
const hoursOfCoding: number = 8
or string
:
const favouriteQuote: string = ‘Fortune favors the prepared mind.’
or array
:
const bestGames: string[] = [‘Guitar Hero’, ‘Mortal Kombat’, ‘Need for Speed’]
and so on.
This is an easy and clear convention to follow with simple variables and small school projects. Now let’s say we’re working with an API that responds with a more complex structure, like this:
User
{
id: 3601,
name: 'John Doe',
yearOfBirth: 1992,
favouriteMovies: ['Home Alone', 'E.T.', 'Die Hard']
orders: [
{
id: 234,
title: 'Home Alone',
price: 99,
currency: 'USD',
onSale: true
},
{
id: 235,
title: 'Forrest Gump',
price: 199,
currency: 'USD',
onSale: false
}
]
}
When we use a particular service or make an API call, the response will always follow the same pattern. The user in our example will always have an id, a name, a birth year, and so on, so we should create a custom type for this. Not only because it’s a great feeling to put the pieces of the application together, but because later the editor will assist us with beautiful error messages like
Property 'yearOfBirth, ...' is missing in type '{ name: string; }' but required in type 'User'.
so we will be able to easily figure out how to build a service function that returns a user.
Alright, let’s get into it.
Creating a new custom type is really easy, just use the type
word to declare it.
type User
Please be aware that the naming convention used here is UpperCamelCase. The next thing to consider is the outermost layer of our value, which in this case is an object:
type User = {}
Our custom type is now ready to use; the editor will not give us an error message if we try to declare a variable with it but let’s improve it. The next step is to collect and categorize all of our object’s basic type fields:
type User = {
id: number,
name: string,
yearOfBirth: number,
favouriteMovies: string[]
}
Almost there! Now, we can see that the orders
is an array of custom-type objects as well. In this case, we should take advantage of TypeScript’s use-case of array types like string[]
(an array that contains string
values) or number[]
(contains number
values) and create an Order
type and use it the same way: Order[]
type Order = {
id: number,
title: string,
price: number,
currency: string,
onSale: boolean
}
And that is basically it. We have two fully customized types ready to use
type Order = {
id: number,
title: string,
price: number,
currency: string,
onSale: boolean
}
type User = {
id: number,
name: string,
yearOfBirth: number,
favouriteMovies: string[],
orders: Order[]
}
and we can easily type our variable
const userToHandle: User = {
id: 3601,
name: 'John Doe',
yearOfBirth: 1992,
favouriteMovies: ['Home Alone', 'E.T.', 'Die Hard']
orders: [
{
id: 234,
title: 'Home Alone',
price: 99,
currency: 'USD',
onSale: true
},
{
id: 235,
title: 'Forrest Gump',
price: 199,
currency: 'USD',
onSale: false
}
]
}
or later any other user-related variable and state with them.
Optional fields
If you have a property that can be attached to your entity later on a condition, you can use the ?
operator to create optional fields; in our example, on the user
object the orders
or favouriteMovies
fields could be undefined
if the user has not ordered anything yet, so the type must be constructed to work without favouriteMovies
and orders
fields as well like this:
type User = {
id: number,
name: string,
yearOfBirth: number,
favouriteMovies?: string[],
orders?: Order[]
}
In this case, the user
object can be constructed and will be defined without the optional fields as well.
Pipe operator
Assume that some orders have a number
and others have a string
type id
. You can always use the good old any
type
{
id: any
...
}
but that isn’t very professional.
The best solution is to type the id
field using the pipe operator |
because it can be either a number
OR a string
:
type Order = {
id: number | string,
title: string,
price: number,
currency: string,
onSale: boolean
}
Now id
is able to be a number
like 234
or a string
like 'cf41e5bd’
.