Javascript

Why does instanceof in TypeScript give me the error Foo only refers to a type but is being used as a value here

19 September 2026 · 9 min read

Why does instanceof in TypeScript give me the error Foo only refers to a type but is being used as a value here

Have you ever encountered the frustrating TypeScript error: “‘Foo’ only refers to a type, but is being used as a value here” when using the instanceof operator? This is a common stumbling block for developers new to TypeScript, or even experienced JavaScript developers transitioning to TypeScript’s type system. The issue arises from a fundamental difference in how TypeScript handles types versus values at runtime. This error typically pops up when you try to use a type definition directly with instanceof, which expects a constructor function or a class, something that exists as a tangible value during the execution of your code. Understanding why this happens is crucial for writing robust and type-safe TypeScript applications. This article will dive deep into the reasons behind this error, providing clear explanations and practical solutions to help you avoid this pitfall and leverage the power of TypeScript effectively. The goal is not just to fix the error, but to grasp the underlying concepts and write cleaner, more maintainable code.

Understanding TypeScript Types vs. Values

TypeScript introduces the concept of static typing on top of JavaScript. In essence, TypeScript uses types to provide compile-time checks, ensuring that your code adheres to certain contracts before it’s even executed. However, these types are often erased during the compilation process, meaning they don’t exist as concrete values at runtime. This is a key distinction. JavaScript, on the other hand, deals with values – objects, functions, primitives – that exist and are manipulated during program execution. When you use instanceof, you’re asking JavaScript to check if an object was created by a particular constructor function. This requires the right-hand side of instanceof to be a value, specifically a function that can act as a constructor.

The error “‘Foo’ only refers to a type, but is being used as a value here” occurs because TypeScript has identified that you’re attempting to use something defined purely as a type (an interface, a type alias, or a type definition of a class) in a context where a value (a constructor function representing a class) is expected. For example, if you define an interface Foo and then try to use instanceof Foo, TypeScript will throw this error because interfaces are purely type-level constructs; they don’t have a runtime representation as a constructor function. The TypeScript compiler is preventing you from making a mistake that would inevitably lead to runtime errors if the code were allowed to execute as JavaScript.

Consider this simplified example:

interface Animal { name: string; makeSound(): void; } function isAnimal(obj: any): obj is Animal { return obj instanceof Animal; // This will cause the error! } 

In this case, Animal is an interface, which exists only at compile time to enforce type safety. It doesn’t exist as a value during runtime, therefore, it cannot be used with instanceof.

Common Scenarios and Root Causes

The most frequent cause of this error is attempting to use an interface with instanceof. Interfaces, as mentioned earlier, are purely type-level constructs. They define the shape of an object but don’t generate any actual JavaScript code at runtime. Another common scenario involves importing types without also importing the corresponding value. This can happen when you’re working with external libraries or modules that provide both type definitions and runtime implementations. Ensure you’re importing the class or constructor function, not just its type definition.

Type aliases can also lead to this error if you attempt to use them with instanceof. A type alias simply creates a new name for an existing type; it doesn’t create a new value. For instance, type MyString = string; doesn’t create a new constructor function that you can use with instanceof. The key takeaway is to differentiate between what exists solely for type checking and what has a concrete representation at runtime. Using a class with a type definition can also be the cause. If you define a class but only import the type definition, the same error will be thrown.

Here’s a list summarizing the primary causes:

  • Using an interface with instanceof.
  • Using a type alias with instanceof.
  • Importing only the type definition of a class, not the class itself.
  • Attempting to use a type guard where a runtime check is required.

Solutions and Workarounds

The solution depends on the underlying reason for the error. If you’re trying to check if an object conforms to a particular interface, you’ll need to implement a custom type guard function. A type guard is a function that narrows down the type of a variable within a specific block of code. This function returns a boolean, indicating whether the object matches the expected type. This is because TypeScript interfaces are design-time constructs and disappear at runtime. Therefore, the instanceof operator will not work with interfaces.

If you need to check the type of an object at runtime, ensure that you’re using a class (which has a constructor function) with instanceof. If you’re working with external libraries, double-check your imports to make sure you’re importing the class itself, not just its type definition. Here’s an example of a custom type guard:

interface Animal { name: string; makeSound(): void; } function isAnimal(obj: any): obj is Animal { return typeof obj === 'object' && obj !== null && 'name' in obj && typeof obj.name === 'string' && 'makeSound' in obj && typeof obj.makeSound === 'function'; } 

This isAnimal function checks if the provided object has the properties required by the Animal interface. This approach allows you to perform runtime type checking in a type-safe manner. Remember that type guards provide runtime checks, which are necessary because TypeScript types are erased during compilation.

Best Practices for Avoiding the Error

To avoid the “‘Foo’ only refers to a type, but is being used as a value here” error, follow these best practices:

  1. Use classes with instanceof: Only use classes (which have constructor functions) with the instanceof operator.
  2. Implement custom type guards for interfaces: For interfaces, create custom type guard functions to perform runtime type checking.
  3. Double-check your imports: Ensure you are importing the class or constructor function, not just its type definition, especially when working with external libraries.
  4. Understand the difference between types and values: Be aware that TypeScript types are compile-time constructs, while JavaScript values exist at runtime.

By adhering to these guidelines, you’ll minimize the chances of encountering this error and write more robust and maintainable TypeScript code. Furthermore, documenting your code clearly, especially around type guards and complex type relationships, can significantly help other developers (and your future self) understand and maintain your codebase. Consider using tools like JSDoc to generate documentation from your TypeScript code.

Here are some key points to remember:

  • TypeScript types are erased during compilation.
  • instanceof requires a constructor function (a value) on the right-hand side.
  • Custom type guards provide runtime type checking for interfaces.

FAQ

Why can't I use interfaces with `instanceof`?
Interfaces in TypeScript are purely type-level constructs. They define the shape of an object for type checking but don't exist as values at runtime. `instanceof`, on the other hand, requires a constructor function (a value) to perform its check.
What is a custom type guard?
A custom type guard is a function that narrows down the type of a variable within a specific block of code. It returns a boolean, indicating whether the object matches the expected type. They're crucial for runtime type checking when working with interfaces or complex type relationships.
How do I import the correct thing from a library?
Carefully examine the library's documentation. Ensure you're importing the class or constructor function, not just its type definition. Some libraries might export types and values with different names, so pay close attention to the import statements.
Is there a performance overhead to using custom type guards?
Yes, there is a slight performance overhead because custom type guards perform runtime checks. However, this overhead is usually negligible compared to the benefits of increased type safety and reduced runtime errors, especially in complex applications. Consider using [memoization techniques](https://courthousezoological.com/n7sqp6kh?key=e6dd02bc5dbf461b97a9da08df84d31c) if the performance of a specific type guard becomes a bottleneck.
By understanding the nuances of TypeScript's type system and the distinction between types and values, you can effectively avoid the "'Foo' only refers to a type, but is being used as a value here" error and write more reliable code. Remember to leverage custom type guards for interfaces, ensure you're importing the correct entities from libraries, and always prioritize type safety in your TypeScript projects. The goal should be not only to fix the error but to understand the underlying principles that prevent it from occurring in the first place. This increased understanding will enable you to design and implement more robust, maintainable, and type-safe applications. Consider exploring advanced type manipulation techniques, such as conditional types and mapped types, to further enhance your TypeScript skills and create more sophisticated type definitions. As TypeScript continues to evolve, staying informed about the latest best practices and language features is essential for maximizing its benefits. Explore resources like the official TypeScript documentation [TypeScript Documentation](https://www.typescriptlang.org/docs/), Stack Overflow [Stack Overflow](https://stackoverflow.com/), and articles on Medium [Medium](https://medium.com/) to continue learning.

Question & Answer :
I wrote this code

interface Foo { abcdef: number; } let x: Foo | string; if (x instanceof Foo) { // ... } 

But TypeScript gave me this error:

'Foo' only refers to a type, but is being used as a value here. 

Why is this happening? I thought that instanceof could check whether my value has a given type, but TypeScript seems not to like this.

TL;DR

instanceof works with classes, not interfaces nor type aliases.


What’s TypeScript trying to tell me?

The issue is that instanceof is a construct from JavaScript, and in JavaScript, instanceof expects a value for the right-side operand. Specifically, in x instanceof Foo JavaScript will perform a runtime check to see whether Foo.prototype exists anywhere in the prototype chain of x.

However, in TypeScript, interfaces have no emit. The same is true of type aliases. That means that neither Foo nor Foo.prototype exist at runtime, so this code will definitely fail.

TypeScript is trying to tell you this could never work. Foo is just a type, it’s not a value at all!

If you’re coming from another language, you might have meant to use a class here. Classes do create values at runtime, but there are some notes about that that you may want to read about below.

“What can I do instead of instanceof if I still want a type or interface?”

You can look into type guards and user-defined type guards.

“But what if I just switched from an interface to a class?”

You might be tempted to switch from an interface to a class, but you should realize that in TypeScript’s structural type system (where things are primarily shape based), you can produce any an object that has the same shape as a given class:

class C { a: number = 10; b: boolean = true; c: string = "hello"; } let x = new C() let y: C = { a: 10, b: true, c: "hello", } // Works! x = y; y = x; 

In this case, you have x and y that have the same type, but if you try using instanceof on either one, you’ll get the opposite result on the other. So instanceof won’t really tell you much about the type if you’re taking advantage of structural types in TypeScript.