Typescript Logo

TYPESCRIPT

You should avoid these Typescript Mistakes

Kadir Osman Ust
4 min readFeb 26, 2023

--

Hello guys! I haven’t written for a long time. I hope you will enjoy this article. This article will be about Typescript’s mistakes. Let’s start.

Use ‘unknown’ instead of ‘any’

any and unknown are the same in terms of what is assignable to them, different in that unknown is not assignable to anything except any. So Let’s look at these two types

‘any’ Type

any, that you can use whenever you don’t want a particular value to cause type-checking errors.

When a value is of type any, you can access any properties of it, call it like a function, assign it to (or from) a value of any type, or pretty much anything else.

‘unknown’ Type

The unknown type represents any value. This is similar to any type, but is safer because it’s not legal to do anything with an unknown value:

function f1(a: any) {
a.b(); // OK
}
function f2(a: unknown) {
a.b(); // We'll get an error.
// Object is of type 'unknown'.
}

In this situation, We should use unknown if we don’t want a runtime error.

Let’s look at a real example between two types;

/*
We have a fetch structure for getting data from Backend.
Also, We have a User interface and an AdminUser interface.
We send a request and get the User but,
we don’t know the user’s type, Right?
So We should use the Unknown type for that.
*/

interface IUser{
id: string;
firstName: string;
lastName: string;
email: string;
userName: string;
}

interface IAdminUser extends IUser {
token: string;
addNewUser: ()=>void;
}

function isAdminUser(object: unknown): object is IAdminUser {
if(object !==null && typeof object === "object"){
return "token" in object;
}
return false;
}

function isNormalUser(object: unknown): object is IUser {
if(object !==null && typeof object === "object"){
return "token" !in object;
}
return false;
}

async function fetchUser(){
const response = await fetch("localhost:3000/getUser/1");

//Bad using
const badUser: any = await response.json();
/*
I can acsess and Dont get an error but We dont
known the user type and If the user is normal user
I'll get an error on runtime.
*/
badUser.addNewUser()
//------

// Best Practice

const bestUser : unknown = await response.json();
if(isAdminUser(bestUser)){
bestUser.addNewUser() //We'll not get an error. And We can know the type.
}
if(isNormalUser(bestUser)){
bestUser.addNewUser() // We got an error when we coding, not at runtime.
}
}

Use ‘is’ operator

Boolean is just a data type while isoperator is used for type-testing. When we use the is operator, We say the typescript ‘hey! check this out’

Let’s look at this closer;

type Species = 'cat' | 'dog';

interface Pet {
species: Species;
}

class Cat implements Pet {
public species: Species = 'cat';
public meow(): void {
console.log('Meow');
}
}

function petIsCat(pet: Pet): pet is Cat {
return pet.species === 'cat';
}

function petIsCatBoolean(pet: Pet): boolean {
return pet.species === 'cat';
}

const p: Pet = new Cat();

p.meow(); // ERROR: Property 'meow' does not exist on type 'Pet'.

if (petIsCat(p)) {
p.meow();
// now compiler knows for sure that the variable is of type Cat and it has meow method
}

if (petIsCatBoolean(p)) {
p.meow(); // ERROR: Property 'meow' does not exist on type 'Pet'.
}

Use the ‘satisfies’ operator

The purpose of satisfies is to enforce a constraint on a variable, without changing its type.

interface ICustomImage{
data: string;
width: number;
height: number;
}

type UserImage = string | ICustomImage;

interface IUser{
id: number;
firstName: string;
lastName: string;
image: UserImage;
}

//bad usage
const badUser : IUser= {
id:1,
firstName: 'Kadir Osman',
lastName: 'Ust',
image: 'image/url'
}

// You cannot know the type of the image and cannot use the string methods as well.
badUser.image

const gooUser = {
id:1,
firstName: 'Kadir Osman',
lastName: 'Ust',
image: 'image/url'
} satisfies IUser;

// Now, You can use string method.

Use the Utility Types

There are many utility types. We will not talk about all of them. But some of them are really important for us. These’re Partial<type>, Required<type>, Omit<type,keys>, Readonly<type>; Let’s look at these;

What is Partial<type>?

Partial is a type creator. Constructs a type with all properties of type set to optional. This utility will return a type that represents all subsets of a given type. It’s behaving like all properties are optional. Let’s look at it closely;

type User ={
username : string
name : string
}

const ourUser: Partial<User> = {
name: 'Kadir'
}

What is Required<type>?

Constructs a type consisting of all properties of type set to required. The opposite of Partial<type>.

interface Props {
a?: number;
b?: string;
}

const obj: Props = { a: 5 };

const obj2: Required<Props> = { a: 5 };
/*
error: Property 'b' is missing in type '{ a: number; }'
but required in type 'Required<Props>'.
*/

What is Omit<type, keys>?

Constructs a type by picking all properties from type and then removing keys (string literal or union of string literals).

Let’s look at the example. There are too many properties. When we don’t want to send id prop and create a new type or interface for just it, we can omit it. For example ;

interface IFirm {
id: string;
name: string;
}

const ourData : Omit<IFirm,'id'> = {
name : 'Kardesler Yazilim'
}

What is Readonly<type>?

Constructs a type with all properties of Type set to readonly, meaning the properties of the constructed type cannot be reassigned.

interface Todo {
title: string;
}

const todo: Readonly<Todo> = {
title: "Delete inactive users",
};

todo.title = "Hello";
/*
Cannot assign to 'title' because it is a read-only property.
*/

This utility is useful for representing assignment expressions that will fail at runtime (i.e. when attempting to reassign properties of a frozen object).

Hope to see you in other articles.

--

--

Kadir Osman Ust
Kadir Osman Ust

Written by Kadir Osman Ust

Senior Frontend Engineer @Getir - Javascript, Typescript, React, Next.js

No responses yet