Hello there ๐ I am Eslam Ahmed SW engineer and frontend passionate, Welcome to the third article in this series about Typescript. I am very glad to see new readers here
You can check the previous articles:
In the last article we have talk about:
- Why to use types
- Basic types
- How to add types to functions
- Some utility types and
never - void - any
types - Literal types
- Object types
- finally, we wrote our first algorithm (Binary Search) in Typescript
It was very rich article make sure you check it if you didn't yet.
Today We are going to know more about types but this time about custom types and here is our index:
- What are custom types and Why to use them.
- Defining custom types with keyword
type
๐ - Defining custom types with keyword
class
โ๏ธ - Defining custom types with keyword
interface ๐
- When to use each of them?
- Union of Custom Types ๐ค
- Unknown โ
Custom Types
TypeScript is a superset of JavaScript that gives you the ability of using all the features of JavaScript in runtime and adds a type checks at compile time, That type checking prevent lots of bug.
Custom Types gives you the ability of creating defining types from other types whether basic types or other custom types. now let's see how to define those types
Defining Types Using type
Keyword
The type keyword allows you to declare a new type or a type alias for an existing type. Ypu have already seen the simple using of type in last article whe we defined an object, Now let's see more concise example. Suppose we are coding a geometry app and we want to represent points, rectangles and circles in 2d grid.
- Point: described with x, y values
- Rectangle : defined with the bottom left and top right points
- Circle: defined with the center point and the radius
Type definition
//define the point type
type Point = {
x : number,
y : number
}
// define rectangle type using put point type
type Rectangle= {
bottomLeft: Point,
topRight: Point
}
// define circle type
type Circle = {
center: Point,
raduis: number
}
Usage
const originPoint : Point = {
x : 0,
y : 0
}
let p1 : Point = {x:1 ,y:1}
let rect1 : Rectangle = {
topRight : p1,
bottomLeft : originPoint
}
As always If we forgot to add an property or used wrong type an error will be shown, Here I intentionally forget to add the radius while declaring circle
You can although make some properties optional using ?
operator, the following example assumes we are defining user type to hold date retrieved from database, each user has name, id and optional image url
type User = {
id: number,
name: string,
imgURL?: string
}
as you can see now errors
Also functions could have type aliases, going back to our geometry app example and add function to draw a point
const drawPoint = (p : Point) => {
//do some magic to draw point
if (magicWorks) {
return true;
}
return false;
}
type PointDrawer = (p : Point) => boolean
const drawLine= (slope:number, drawer:PointDrawer) => {
//do some magic to draw line using y=mx
}
More about type key word and type aliases will be mentioned eventually but let's move to next jey word that is used for defining types, make some coffee โ๏ธ and join me.
Defining Types Using class
Keyword
Here you can create an ES6 class to define the type, If you are new to the concept of oop and ES6 classes you could check Omotola's article or Faheem's article. In Javascript ES6 we create new class as follow
"use strict";
class Person {
constructor(firstName, lastName, age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
};
}
const lang = new Person("Java", "Script", 26);
In TypeScript you don't need to explicitly write the constructor body, the compile will do it instead of us "Thanks mr tsc
"
class Person {
constructor(public firstName: string,
public lastName: string, public age: number) {};
}
const lang = new Person("Type", "Script", 8);
More about classes and oop paradigm are and how to use them in TypeScript in the next set of articles.
Defining Types Using interface
Keyword
Interface is a structure that defines the contract in your application. It defines the syntax for classes to follow. Classes that are derived from an interface must follow the structure provided by their interface.
- from Tutorial Teacher
In English you only type the properties and function signature, function implementation is written in the inherited classes.
Many Programming language such as Java and PHP. JavaScript didn't supported it yet however Typescript implements it and you can use it, just wait for the next few articles. now we are going to use it only in defining custom types.
interface Person {
firstName:string,
lastName:string,
age:number
}
const p: Person = {
firstName: "Osama",
lastName: "Ahmed",
age:23
}
When to use each of them?
The following image shows each one of the three key words type - class - interface
on the left and the JavaScript code the compiled to on the right try to play with the code here .
here is three tips for selecting the proper keyword in type definition
- If you only need to declare types for type checking use
type
orinterface
- If you want this type to represent a value in runtime use
class
- types defined
type
keyword support intersection and unions between them.
Union of custom types.
We have already talk about type unions in the previous article. as a refresh type union enables you to define variable that can take to types such as number or string
let username: string|number;
Custom types union is almost the same, here is a great example from Typescript Quickly
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Rectangle | Circle;
function area(shape: Shape): number {
switch (shape.kind) {
case "rectangle": return shape.height * shape.width;
case "circle": return Math.PI * shape.radius ** 2;
}
}
// note x ** 2 <=> x power 2 <=> x * x
Another way to check the actual type is using in
keyword.
in
: checks if specific property exists in type. the next code snippet is a refactoring to the previous area
function using in
keyword, it may be not the optimal implementation but it fulfills the purpose
function area(shape: Shape): number {
//check if the shape has the property radius
if ("radius" in shape){
return Math.PI * shape.radius ** 2;
}
// if not so it's rectangle
return shape.height * shape.width;
}
unknown
Type in TypeScript
While using TypeScript you tend to annotate every variable with it's type or possible types using unions but sometimes you don't know the type or you just migrated large code from JavaScript into TypeScript and want to run this code without refactoring and annotating every variable a solution would be using any
type whether implicitly var x
or explicitly x : any= "delightful"
. However any must be your last resort as it declines all type checking and brings back all JavaScript downsides.
unknown
is very similar to any
but it's more safe to be used. let's see some examples to clarify that
1- you can assign any type to both of them
// It is ok to change the type for both
let varAny:any = "ANY"
varAny = 5
let varUnknown: unknown = "UNKNOWN"
varUnknown = 5
2- variable of type any could be assigned to annotated variable (string for example)
let varStr : string
let varNum:number
varStr = varAny
/*
Typescript is being optimistic that varAny will be string at runtime
which is wrong as varAny is number but no compilation error is shown
*/
3- variable of type uknnown could not be assigned to annotated variable
4- you can access imaginary members or function of variable of type any and no errors will be detected until runtime
varAny.isTrue.lst.forEach((x:number):number => x**2)
5- It's clear now how bad any is and how unknown is safer, but how to use unknown?
- to use variable of type unknown you need to make type checking | narrowing first
That's it for today I hope you enjoyed this article and learnend new things if so don't forget to like, comment and share. Constructive Feedbacks are very welcomed even in commints section or messages on twitter, cheers ๐