Last updated: Feb 29, 2024
Reading timeยท3 min
The 'instanceof' error "only refers to a type, but is being used as a value
here" occurs when we try to use the instanceof
operator with a type instead of
a value.
To solve the error, create a class instead, or simply use a user-defined type guard.
Here is an example of how the error occurs.
interface Employee { name: string; salary: number; } let emp: Employee; // โ๏ธ 'Employee' only refers to a type, but is being used as a value here.ts(2693) if (emp instanceof Employee) { console.log(emp.salary); }
The error was caused because we used the instanceof operator with a type instead of a value.
instanceof
operator returns a boolean value indicating whether the prototype property of the constructor appears in the prototype chain of the object.instanceof
with a class insteadOne way to solve the error is to use a class instead of an interface.
class Employee { constructor(public name: string, public salary: number) { this.name = name; this.salary = salary; } } const emp = new Employee('Bobby Hadz', 100); if (emp instanceof Employee) { // ๐๏ธ this runs console.log('โ is instance of Employee'); } console.log(emp instanceof Employee); // ๐๏ธ true
The emp
object was created using the Employee
class, so it is an instance of
the class.
You can also set class properties if you don't intend to take parameters when instantiating the class.
class Employee { name = ''; salary = 0; tasks: string[] = []; } const emp = new Employee(); emp.name = 'Bobby Hadz'; emp.salary = 100; emp.tasks.push('test'); if (emp instanceof Employee) { // ๐๏ธ this runs console.log('โ is instance of Employee'); } console.log(emp instanceof Employee); // ๐๏ธ true
However, you can have an object that satisfies the type but isn't an instance of the class because it wasn't created using the specific class.
class Employee { constructor(public name: string, public salary: number) { this.name = name; this.salary = salary; } } // ๐๏ธ Correct type, but not an instance const emp2: Employee = { name: 'Bobby Hadz', salary: 200, }; console.log(emp2 instanceof Employee); // ๐๏ธ false const emp = new Employee('Tom', 100); console.log(emp instanceof Employee); // ๐๏ธ true
The emp2
variable is not an instance of the Employee
class, even though it
satisfies the type. The reason is that it wasn't created using the class.
instanceof
If you need to test if a specific object correctly implements a type, use a user-defined type guard.
interface Employee { id: number; name: string; salary: number; } function isAnEmployee(obj: any): obj is Employee { return 'id' in obj && 'name' in obj && 'salary' in obj; } const emp: Employee = { id: 3, name: 'Bobby Hadz', salary: 300, }; console.log(isAnEmployee(emp)); // ๐๏ธ true console.log(isAnEmployee({ id: 1 })); // ๐๏ธ false if (isAnEmployee(emp)) { // ๐๏ธ TypeScript knows that emp is type Employee console.log(emp.id); // ๐๏ธ 3 console.log(emp.name); // ๐๏ธ "Bobby Hadz" console.log(emp.salary); // ๐๏ธ 300 }
We used a user-defined type guard to check if an object implements an interface.
obj is Employee
syntax is a type predicate where obj
must be the name of the parameter the function takes.If the isAnEmployee
function returns true
, TypeScript knows that the passed
in value is of type Employee
and allows us to access all properties and
methods on the specific interface.
The example above simply checks if the passed-in object contains the id
,
name
and salary
properties.
This can get pretty verbose if your interface has many properties.
type
property to the interfaceAn alternative approach is to
add a type
property to the interface,
for which you check instead.
interface Employee { id: number; name: string; salary: number; type: 'Employee'; // ๐๏ธ add type property } function isAnEmployee(obj: any): obj is Employee { // ๐๏ธ check for type property return 'type' in obj && obj.type === 'Employee'; } const emp: Employee = { id: 3, name: 'Bobby Hadz', salary: 300, type: 'Employee', }; console.log(isAnEmployee(emp)); // ๐๏ธ true console.log(isAnEmployee({ id: 1 })); // ๐๏ธ false if (isAnEmployee(emp)) { console.log(emp.id); // ๐๏ธ 3 console.log(emp.name); // ๐๏ธ "Bobby Hadz" console.log(emp.salary); // ๐๏ธ 300 console.log(emp.type); // ๐๏ธ "Employee" }
The Employee
interface has a type
property with the string literal value of
Employee
. This means that all objects that have a type of Employee
will have
this property.
In our function, all we have to do is check if the passed-in object has a type
property that's equal to Employee
.
You can learn more about the related topics by checking out the following tutorials: