Over the past decade, the adoption of Typescript has been a major breakthrough in web development. Employers are increasingly seeking developers with knowledge of Typescript to prevent technical debt. Unlike dynamically-typed languages that offer ease in development, Typescript helps prevent runtime errors and bugs. Therefore, learning Typescript has become essential for web developers looking to secure job opportunities.
That being said, making the transition from javascript to typescript is a daunting task. More so if one is not familiar with any statically typed language. In this article, I'm gonna share my experience in solving problems with typescript.
A Little Intro to Typescript
Typescript is a superset of javascript. This means that typescript incorporates everything javascript has to offer and has its own type system. This type system is largely what allows for more efficient and secure coding practices, reducing the likelihood of errors and improving the overall quality of the code. Throughout this article, there would be various code examples. If you wanna follow along (which I highly recommend you do) head over to official typescript playground.
The basics !
Let's start with the primitives first and look at some code.
const x: string = "hello"
const y: number = 3
const z: boolean = true
This part is rather intuitive. By applying types to variables, you can put restrictions over the type of data that variable holds. Deviating from the defined type will throw errors!
// Type 'number' is not assignable to type 'string'.
const x: string = 42
This is the very basics of typescript and everything that's gonna lie ahead, follows this core fundamental; albeit in much more complex ways.
Types and Interfaces
In real life project, we very often find the need to represent real world objects in terms of code. Types and interfaces help us represent exactly that. Let's take an example of a simple shopping cart. In our cart, we would be placing items and when our shopping would be over, we gotta do a checkout and pay the total amount!
Pro Tip: THINK about the properties and type before you write even a single line of code.
We will be defining exactly what our cart items look like. Let's say they have a name, price and quantity.
// The id lets us uniquely identify our items!
type CartItem = {
id: number;
name: string;
price: number;
quantity: number;
}
Now, shopping cart will be just an array of these cart items.
interface CartItem {
id: number;
name: string;
price: number;
quantity: number;
}
type ShoppingCart = CartItem[];
Interfaces vs Types - which to use and when?
Both of them basically do the same thing. While starting out, it's more of a personal preference. However, it's important to be consistent in your codebase and choose a style that works best for you.
Personally, I use interfaces to describe shape of any real world object and only use types if I need to apply utilities on it like intersection and union. You don't need to worry about these stuff if you are new to typescript.
Functions and how to type them
Let's see an example of a simple sum function. Try to
function sumFn(a: number, b: number):number {
return a + b
}
const sumArrowfn = (a: number, b: number):number => a + b
as you can see, we can type annotate the parameter a:number
just like we have been doing before. We can also specify the return type of that function after the closing parenthesis.
Adding functionality to our cart
Now that we understand how to type a function, let's add some functionality to our cart. We will be implementing the following 2 features -
- add items to the cart
- delete items from the cart
Add items to cart
How can we add items to a cart ? We will provide a Cart and an Item. Our expectation from the function would be that it returns us a Cart array with the item. But what will happen if the item we are gonna add already exist in the cart ? In that case, we would simply increment the quantity. So, the simplified logic would be like this -
If the item does not exist previously in the Cart, push the item into the Cart.
If the item does exist, we simply increment the quantity of the item in the Cart.
Now that we have our logic defined, we can easily type annotate the parameters of the function and also specify it's return type.
Let's see the code in action:
interface CartItem {
id: number;
name: string;
price: number;
quantity: number;
}
type ShoppingCart = CartItem[];
const addItem = (cart: ShoppingCart, item: CartItem): ShoppingCart => {
const existingItem = cart.find((i) => i.id === item.id);
if (existingItem) {
existingItem.quantity += item.quantity;
} else {
cart.push(item);
}
return cart;
}
If you are in a typescript playground or your local editor, try hovering over the first existingItem
. It will show you the type as CartItem | undefined
. This is because the find method returns undefined
if there are no matching elements and typescript understands this. Here, if instead of that if
check, I write a buggy code cart.push(existingItem);
, typescript will immediately throw an error:
Typescript throws this error cause existingItems
may contain undefined
but our type definition for the return type of the addItem
function was ShoppingCart
.
Typescript forces us to think about the corner cases and handle the code according to the type definitions we set up. This is one of the main pros of typescript.
Remove product from cart
This is straightforward. We pass the id
of the item to be deleted and the cart
. From the id
we can get the index of the item to be deleted and then just remove it using splice
.
Let's see that in action:
interface CartItem {
id: number;
name: string;
price: number;
quantity: number;
}
type ShoppingCart = CartItem[];
const addItem = (cart: ShoppingCart, item: CartItem): ShoppingCart => {
const existingItem = cart.find((i) => i.id === item.id);
if (existingItem) {
existingItem.quantity += item.quantity;
} else {
cart.push(item);
}
return cart;
}
const removeItem = (cart: ShoppingCart, id: number): ShoppingCart => {
const itemIndex = cart.findIndex((i) => i.id === id);
if (itemIndex >= 0) {
cart.splice(itemIndex, 1);
}
return cart;
}
Full code in action
Here's the little shopping cart we have built. Can you implement a function that returns the all total price of the items in the cart ? Comment down the solution !
interface CartItem {
id: number;
name: string;
price: number;
quantity: number;
}
type ShoppingCart = CartItem[];
const addItem = (cart: ShoppingCart, item: CartItem): ShoppingCart => {
const existingItem = cart.find((i) => i.id === item.id);
if (existingItem) {
existingItem.quantity += item.quantity;
} else {
cart.push(item);
}
return cart;
}
const removeItem = (cart: ShoppingCart, id: number): ShoppingCart => {
const itemIndex = cart.findIndex((i) => i.id === id);
if (itemIndex >= 0) {
cart.splice(itemIndex, 1);
}
return cart;
}
let cart: ShoppingCart = [];
cart = addItem(cart, { id: 1, name: "Cheese", price: 10, quantity: 2 });
cart = addItem(cart, { id: 2, name: "Chocolate", price: 20, quantity: 1 });
console.log(cart)
cart = removeItem(cart, 2)
console.log(cart)
Conclusion
The stuff mentioned in this blog just scratches the surface of what Typescript is capable of. There are more advanced features like Generics, type Utilities, type guards and type inferences, things like any, never and unknown types, etc. Further blog posts will illustrate how to use them and the mental models that helped me to apply these concepts to real world projects.
Top comments (0)