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();
}
}
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();
}
}
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
}
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.
}
That is it. Now we can have similar behavior to the instance of the interface in TypeScript.
Thank you for reading!
Top comments (2)
Try this:
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.