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.
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.
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();
}
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'Point' only refers to a type, but is being used as a value here.
TS2693: 'Handle' only refers to a type, but is being used as a value here
'SessionProvider' only refers to a type, but is being used as a value here. In Typescript project
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?
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.
On mobile so a shot in the dark here, but the type for symbols is actually lower case. So let x: symbol = Symbol ("foo"); may do what you want.
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?
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
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