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.

Answer from Daniel Rosenwasser on Stack Overflow
Top answer
1 of 6
231

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.

2 of 6
41

To do type checking at runtime with an interface is using type guards, if interfaces you wish to check have different properties/functions.

Example

let pet = getSmallPet();

if ((pet as Fish).swim) {
    (pet as Fish).swim();
} else if ((pet as Bird).fly) {
    (pet as Bird).fly();
}
🌐
GitHub
github.com › typestack › class-transformer › issues › 1359
question: 'T' only refers to a type, but is being used as a value here. · Issue #1359 · typestack/class-transformer
October 9, 2022 - class Quote { ask_price: string ask_size: number bid_price: string bid_size: number last_trade_price: string last_extended_hours_trade_price: string last_non_reg_trade_price: string previous_close: string adjusted_previous_close: string previous_close_date: string symbol: string trading_halted: boolean has_traded: boolean last_trade_price_source: string last_non_reg_trade_price_source: string updated_at: string instrument: string instrument_id: string state: string } const baseRequest = async<T> (token: string, url: string): Promise<T> => { console.log({ message: 'baseRequest', url }) assert.o
Author   brandonros
Discussions

error TS2585: 'Symbol' only refers to a type, but is being used as a value here.

tsc symbols.ts does not use your tsconfig.json file, it uses the compiler's default settings instead. This means it's targeting ES5 (instead of ES2016), which does not define a Symbol constructor, which leads to this error.

You'll want to pass the --project (shorthand -p) option to tsc to use the settings from your tsconfig.json file, something like: tsc --project tsconfig.json symbols.ts (If you want, you can shorten this to: tsc -p . symbols.ts). If you want to compile all files in your project, you can leave out the file path: tsc -p .

See the CLI docs for more information.

More on reddit.com
🌐 r/typescript
16
9
December 30, 2020
'Point' only refers to a type, but is being used as a value here.
AR: error with ts 2.0.3: Cannot find name 'Point'. error with ts 2.1.0-dev.20161023: 'Point' only refers to a type, but is being used as a value here. More on github.com
🌐 github.com
8
October 24, 2016
TS2693: 'Handle' only refers to a type, but is being used as a value here
What platform were you using when you found the bug? React Flow version: 12.3.5 Browser and version: Google Chrome Version 131.0.6778.71 (Official Build) (arm64) OS and version: MacOs Sonoma Versio... More on github.com
🌐 github.com
10
November 26, 2024
'SessionProvider' only refers to a type, but is being used as a value here. In Typescript project
'SessionProvider' only refers to a type, but is being used as a value here. More on github.com
🌐 github.com
15
October 23, 2021
🌐
Edureka Community
edureka.co › home › community › categories › typesript › only refers to a type but is being used as a...
Only refers to a type but is being used as a value here | Edureka Community
June 7, 2022 - interface Foo { abcdef: number; } let x: Foo | string; if (x instanceof Foo) { // ... } But ... refers to a type, but is being used as a value here.
🌐
Reddit
reddit.com › r/typescript › error ts2585: 'symbol' only refers to a type, but is being used as a value here.
r/typescript on Reddit: error TS2585: 'Symbol' only refers to a type, but is being used as a value here.
December 30, 2020 -

Hi all,

I'm getting this error:

☺  tsc symbols.ts                                                                                       
symbols.ts:1:20 - error TS2585: 'Symbol' only refers to a type, but is being used as a value here. Do you need to change your target library? Try changing the `lib` compiler option to es2015 or later.

1 var sym1: Symbol = Symbol("abc");
                     ~~~~~~

symbols.ts:2:20 - error TS2585: 'Symbol' only refers to a type, but is being used as a value here. Do you need to change your target library? Try changing the `lib` compiler option to es2015 or later.

2 var sym2: Symbol = Symbol("abc");
                     ~~~~~~
Found 2 errors.

when I compile this file:

// symbols.ts
var sym1: Symbol = Symbol("abc");
var sym2: Symbol = Symbol("abc");

console.log(sym1 === sym2);

My tsconfig.json looks like this:

{
  "compilerOptions": {
    "module": "commonjs",
    "moduleResolution": "node",
    "target": "es2016",
    "lib": [
      "es6",
      "dom"
    ],
    "sourceMap": true,
    "types": [
      // add node as an option
      "node",
      "reflect-metadata"
    ],
    "typeRoots": [
      // add path to @types
      "node_modules/@types"
    ],
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "resolveJsonModule": true
  },
  "exclude": [
    "node_modules"
  ]
}

The code is taken from a tutorial on Symbols.

Any idea how can I fix the error?

🌐
Bobby Hadz
bobbyhadz.com › blog › typescript-instanceof-only-refers-to-type-but-is-being-used-as-value
(instanceof) 'X' Only refers to a type, but is being used as a value here | bobbyhadz
February 29, 2024 - 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.
🌐
JanBask Training
janbasktraining.com › community › java › how-can-i-solve-the-issue-of-only-refers-to-a-type-but-is-being-used-as-a-value-here-in-typescript
How can I solve the issue of “only refers to a type, but is being used as a value here” in TypeScript? | JanBask Training Community
May 24, 2024 - Const instance = new MyClass(); // Correct usage as a type definition Const another instance = MyClass; // Incorrect usage as a value instead of a type · To resolve this particular issue, you should follow the following points or steps:- ... Firstly, ensure that you have used the class name correctly. It should be used with the ‘new’ keyword for creating the Instances. ... Ensure that the assignment is intended for creating an instance and should not try to reference the class name itself.
🌐
GitHub
github.com › Microsoft › TypeScript › issues › 11807
'Point' only refers to a type, but is being used as a value here. · Issue #11807 · microsoft/TypeScript
October 24, 2016 - TypeScript Version: 2.0.3 / nightly (2.1.0-dev.201xxxxx) Code //first.d.ts declare module 'leaflet' { export type Point = { //same result will be with "export interface Point { constructor()..." too new(lat:Number, lng:Number):Point; clo...
Author   gorshkov-leonid
🌐
DEV Community
dev.to › lico › typescript-checking-type-of-value-interface-and-type-4dn8
Typescript: Checking Type of a Value, (interface, type) - DEV Community
November 2, 2022 - 'Cat' only refers to a type, but is being used as a value here. In Typescript, for type checking, you can use is operator for type checking. const isCat = (cat: any): cat is Cat => (cat as Cat).catId !== undefined; console.log(isCat(cat)); Output ...
Find elsewhere
🌐
GitHub
github.com › xyflow › xyflow › issues › 4850
TS2693: 'Handle' only refers to a type, but is being used as a value here · Issue #4850 · xyflow/xyflow
November 26, 2024 - TS2693: 'Handle' only refers to a type, but is being used as a value here#4850 · Copy link · sridharspxrrow · opened · on Nov 26, 2024 · Issue body actions · React Flow version: 12.3.5 · Browser and version: Google Chrome Version 131.0.6778.71 (Official Build) (arm64) OS and version: MacOs Sonoma Version 14.6.1 (23G93) No response ·
Author   sridharspxrrow
🌐
GitHub
github.com › kobaltedev › kobalte › issues › 535
typescript error TS2693: T only refers to a type, but is being used as a value here. · Issue #535 · kobaltedev/kobalte
November 30, 2024 - tsc output node_modules/@kobalte/core/dist/index-1eb52bda.d.ts:14:58 - error TS2693: 'DialogCloseButtonCommonProps' only refers to a type, but is being used as a value here.
Author   dvirtz
🌐
Reddit
reddit.com › r/typescript › "refers to a value, but is being used as a type here." when returning react functional component
r/typescript on Reddit: "refers to a value, but is being used as a type here." when returning React Functional Component
December 18, 2019 -

I'm hoping to get some insight into why I'm getting this error. I have a React functional component, and in a helper function in another file I have a switch that should return that component:

export const generateResponse = messageType => {
  switch (messageType) {
    case "text_message":
      return <TextMessage />
      break;

    default:
      break;
  }
}

But its giving me an error for <TextMessage /> saying 'TextMessage' refers to a value, but is being used as a type here.

What am I doing wrong?

🌐
GitHub
github.com › nestjs › nest › issues › 217
'Body' only refers to a type, but is being used as a value here. · Issue #217 · nestjs/nest
October 28, 2017 - So this happened when I tried to include body as a parameter to a post function, e.g.: @Post() async insertCat( @Body() cat) { return cat; } The error: TSError: ⨯ Unable to compile TypeScript src\modules\cats.controller.ts (10,23): 'Body...
Author   gregordotjs
🌐
Total TypeScript
totaltypescript.com › books › total-typescript-essentials › the-weird-parts
The Weird Parts | Total TypeScript
'processAlbum' refers to a value, but is being used as a type here. Did you mean 'typeof processAlbum'?2749'processAlbum' refers to a value, but is being used as a type here.
Top answer
1 of 2
11

See below for 3.0 solution

Pick is only a type it is not a class, a class is both a type and an object constructor. Types only exist at compile time, this is why you get the error.

You can create a function which takes in a constructor, and returns a new constructor that will instantiate an object with less fields (or at least declare it does):

export class Base {
    public c: number = 0;
    constructor(public a: number, public b: number) {

    }
}


function pickConstructor<T extends { new (...args: any[]) : any, prototype: any }>(ctor: T)
    : <TKeys extends keyof InstanceType<T>>(...keys: TKeys[]) => ReplaceInstanceType<T, Pick<InstanceType<T>, TKeys>> & { [P in keyof Omit<T, 'prototype'>] : T[P] } {
    return function (keys: string) { return ctor as any };
}

export class PartialDescendant extends pickConstructor(Base)("a", "b") {
    public constructor(a: number, b: number) {
        super(a, b)
    }
}

var r = new PartialDescendant(0,1);

type IsValidArg<T> = T extends object ? keyof T extends never ? false : true : true;
type ReplaceInstanceType<T, TNewInstance> = T extends new (a: infer A, b: infer B, c: infer C, d: infer D, e: infer E, f: infer F, g: infer G, h: infer H, i: infer I, j: infer J) => infer R ? (
    IsValidArg<J> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) => TNewInstance :
    IsValidArg<I> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) => TNewInstance :
    IsValidArg<H> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) => TNewInstance :
    IsValidArg<G> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F, g: G) => TNewInstance :
    IsValidArg<F> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F) => TNewInstance :
    IsValidArg<E> extends true ? new (a: A, b: B, c: C, d: D, e: E) => TNewInstance :
    IsValidArg<D> extends true ? new (a: A, b: B, c: C, d: D) => TNewInstance :
    IsValidArg<C> extends true ? new (a: A, b: B, c: C) => TNewInstance :
    IsValidArg<B> extends true ? new (a: A, b: B) => TNewInstance :
    IsValidArg<A> extends true ? new (a: A) => TNewInstance :
    new () => TNewInstance
) : never

For constructors parameters you will loose things like parameter names, optional parameters and multiple signatures.

Edit

Since the original question was answered typescript has improved the possible solution to this problem. With the addition of Tuples in rest parameters and spread expressions we now don't need to have all the overloads for ReplaceReturnType:

export class Base {
    public c: number = 0;
    constructor(public a: number, public b: number) {

    }
}

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
function pickConstructor<T extends { new (...args: any[]) : any, prototype: any }>(ctor: T)
    : <TKeys extends keyof InstanceType<T>>(...keys: TKeys[]) => ReplaceInstanceType<T, Pick<InstanceType<T>, TKeys>> & { [P in keyof Omit<T, 'prototype'>] : T[P] } {
    return function (keys: string| symbol | number) { return ctor as any };
}

export class PartialDescendant extends pickConstructor(Base)("a", "b") {
    public constructor(a: number, b: number) {
        super(a, b)
    }
}

var r = new PartialDescendant(0,1);


type ArgumentTypes<T> = T extends new (... args: infer U ) => any ? U: never;
type ReplaceInstanceType<T, TNewInstance> = T extends new (...args: any[])=> any ? new (...a: ArgumentTypes<T>) => TNewInstance : never;

Not only is this shorter but it solves a number of problems

  • Optional parameters remain optional
  • Argument names are preserved
  • Works for any number of arguments
2 of 2
2

I am a little late to the game here, but there is an alternative and shorter way to do it if you're mainly interested in making intellisense work.

You can extend the base class and then redeclare the members you want to omit as private. This will generate a typescript error, but adding //@ts-ignore will clear it up and shouldn't affect compilation.

This is my preferred way to do it when things are simple. No real overhead here or challenging type syntax. The only real downside here is that adding //@ts-ignore above the extending class could prevent you from receiving other error messages related to incorrectly extending the Base class.

The one advantage to this approach over the accepted "pickConstructor" approach is that this method doesn't generate any extra code. Whereas "pickConstructor" literally exists as a function after compilation that runs during class definition.

class Base
{
    public name:string;   
}

// @ts-ignore
class Ext extends Base
{
    private readonly name:undefined; // re-declare
}

let thing:Ext = new Ext();
// The line below...
// Doesn't show up in intellisense
// complains about privacy
// can't be set to anything
// can't be used as an object
thing.name = "test";   // ERROR