Mayron
Mayron

Reputation: 2394

Using instanceof in TypeScript to find return type of function results in "refers to a type, but is being used as a value here"

I am using TypeScript and have a situation where a function returns a union of two types. It can either return a string or a model but I don't know how to get the type and perform an action based on what type was returned.

How would I do something like this? How are string and IMyModel being used as values in this example (as suggested by the error message)? The error seems confusing to me.

export interface IMyModel {
  id: number;
  name: string;
}

const myFunc = (value: boolean): string | IMyModel => {
  if (value) return "a string";

  return {
    id: 12,
    name: "model",
  };
};

const a = myFunc(true);

if (a instanceof string) {
  console.log("it is a string");
}

if (a instanceof IMyModel) {
  console.log("it is a model");
}

I get the following static errors in visual studio code on the lines using instanceof:

'string' only refers to a type, but is being used as a value here.ts(2693)

'IMyModel' only refers to a type, but is being used as a value here.ts(2693)

enter image description here

I also switched the interface to use "type" but that's the same.

Upvotes: 1

Views: 8063

Answers (3)

Donovan Hiland
Donovan Hiland

Reputation: 1499

Typescript is a superset of javascript and ultimately compiles down to javascript. That being said it's important to think about what the code you are writing will look like as javascript. Here is what this will look like compiled:

const myFunc = value => {
  if (value) return "a string";
  return {
    id: 12,
    name: "model"
  };
};
const a = myFunc(true);
if (a instanceof string) {
  console.log("it is a string");
}
if (a instanceof IMyModel) {
  console.log("it is a model");
}

You'll notice that the types you declared are compiled away. The error is starting to make a bit more sense now. We can't call instanceof on a typescript type because they don't exist as far as the compiled javascript is concerned. You are probably looking for either:

typeof type guards

if (typeof a === 'string') {
  console.log("it is a string");
}

User defined type guards:

const isMyModel = (model: any): model is IMyModel => {
  return typeof model === 'object' && Boolean(model.id && model.name)
}
if (isMyModel(a)) {
  console.log("it is a model");
}

or both!

Upvotes: 1

Aleksey L.
Aleksey L.

Reputation: 37996

Right-hand parameter in object instanceof constructor expression should be constructor/function.

The instanceof operator tests the presence of constructor.prototype in object's prototype chain.

More info here


Back to your example. One of the possible solutions will be testing if typeof a is a 'string':

if (typeof a === 'string') {
  a // a is of type string here
  console.log("it is a string");
} else {
  a // a is IModel
  console.log("it is a model");
}

Playground

More on typeof type guards

Upvotes: 1

Leon
Leon

Reputation: 12491

To check if something is a string you would do it the same way as in JavaScript

if (typeof a === "string")

Interfaces are purely a TypeScript construct and to not exist in the transpiled JavaScript. Since it is a union you can make the assumption that if it is not a string it must be an IMyModel.

Upvotes: 1

Related Questions