DEV Community

Cover image for TypeScript 'instanceof' interface is it possible?
Andrei Kniazev
Andrei Kniazev

Posted on

TypeScript 'instanceof' interface is it possible?

I really like the power of interfaces in TypeScript and its duck typing. But what really bothers me is that I cannot check whether is something an instance of interface.

For example, we have decided to build a role model for the user class. User class will have different roles and based on role type it either has permissions or not. And we decided to describe roles as interfaces.

So I want to have code like this:

class User {
    constructor(readonly role: IReader | IWriter){};
}

interface IReader  {
}

interface IWriter {
    readonly permissions: Array<string>
}

function guardAccess(user: User){
    // Oh no it is not working 😱
    // Error TS2693
    if (user.role instanceof IReader){
      throw new AccessDeniedException();
    }
}

Enter fullscreen mode Exit fullscreen mode

But in TypeScript, it won't work. Because types do not exist in runtime. There is no such thing as an interface in JavaScript.

Ok, so how can we fix this?

We can use string literal types in TypeScript to identify the interface. Let's update our code:

class User {
    constructor(readonly role: IReader | IWriter){};
}

interface IReader  {
    readonly name: "reader",
}

interface IWriter {
    readonly name: "writer"
    readonly permissions: Array<any>
}

function guardAccess(user: User){
    // ✔️ no error
    if (user.role.name === "reader"){
      throw new AccessDeniedException();
    }
}
Enter fullscreen mode Exit fullscreen mode

It allows us to get data that is available only in a certain interface.

// lets create a user with role reader
const user = getUser();
if (user.role.name === "reader"){
    user.role.name;
    user.role.permissions; // 🛑 error
}
else if (user.role.name === 'writer'){
    user.role.name;
    user.role.permissions; // ✔️ no error
}
Enter fullscreen mode Exit fullscreen mode

Also it TypeScript will help us to control the values of the role's name.

if (user.role.name === "editor"){
 // 🛑 Error: TS 2367This condition will always return 'false'
 // since the types '"reader" | "writer"' and '"editor"' 
 // have no overlap.
}
Enter fullscreen mode Exit fullscreen mode

That is it. Now we can have similar behavior to the instance of the interface in TypeScript.

Thank you for reading!

Top comments (2)

Collapse
 
kostyatretyak profile image
Костя Третяк

Try this:

const enum Permission {
  canRead = 1,
  canWrite = 2
}

class User {
  constructor(public permissions: Permission[] = []) {}
}

function guardAccess(user: User) {
  if (!user.permissions.includes(Permission.canWrite)) {
    throw new AccessDeniedException();
  }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
worldpwn profile image
Andrei Kniazev

This article is not about specific domain but about how to check interface at runtime.

And proposed solution works if you don’t need polymorphism.
For example you have role reader that can only read and role writer with claims for what it can write.