error TS2693: 'Product' only refers to a type, but is being used as a value here. is reported when code that runs (an expression) uses a name that exists only for the checker. A @typedef declares a type and nothing at run time, so value instanceof Product has no Product to test against.
The same message shows up when TypeScript syntax is pasted into a .js file: new Set<string>() is read as the comparisons new Set < string > (), and string is a type, not a value. The parser also reports Expression expected. (TS1109) on the empty parentheses.
At a glance
| Code | TS2693 |
|---|---|
| Message | '{0}' only refers to a type, but is being used as a value here. |
| TypeScript's category | error |
| Listed under | Unknown names and members |
| Turned on by | No option: every checked file reports it |
| In .js files | Reported when the file is checked: checkJs in jsconfig.json, or // @ts-check at the top of the file |
The message is TypeScript's own text from its diagnostic messages file (Apache-2.0); {0} is filled in by tsc for each error.
What it looks like
/** @typedef {{ sku: string, price: number }} Product */
/** @param {unknown} value */
export function isProduct(value) {
return value instanceof Product;error TS2693: 'Product' only refers to a type, but is being used as a value here.
}
export const ids = new Set<string>();error TS2693: 'string' only refers to a type, but is being used as a value here.error TS1109: Expression expected.jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} What causes it
- A
@typedefor@callbackname, or a type imported with@import, is used withinstanceof,new, or as a variable. - Type arguments in angle brackets are written in a
.jsfile, where they are not syntax.
How to fix it
Check the shape with a type guard
To test whether a value is a Product, check its properties in a function whose @returns {value is Product} tells the checker what a true result means. If you need instanceof, make Product a class: a class is both a value and a type.
/** @typedef {{ sku: string, price: number }} Product */
/**
* @param {unknown} value
* @returns {value is Product}
*/
export function isProduct(value) {
return typeof value === "object" && value !== null && "sku" in value && "price" in value;
}
/** @type {Set<string>} */
export const ids = new Set();jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} Put type arguments in a JSDoc tag
Instead of new Set<string>(), annotate the variable: /** @type {Set<string>} */ const ids = new Set();. For a generic function call, annotate the result or cast the argument, as the generics page shows.
Under TypeScript 6.0 and 7.0
Every example on this page also runs under TypeScript 6.0.3 and 7.0.2 when this site is built. One of them reports something different from 5.9.3, the version the page describes; the ✗ on its badge links to its entry below.
A @typedef name used as a value is not reported
The pattern: value instanceof Product, where Product is a @typedef, and a type name in a value position (new Set<string>() in a .js file).
Why: 5.9 reports TS2693 ("only refers to a type, but is being used as a value"); TypeScript 7.0.2 reports neither, and keeps only the syntax error TS1109 for the type argument. The port's list of changes does not mention it. The code still fails when it runs. Source: CHANGES.md: changes between TypeScript 6 and the native port.
Using a @typedef name where JavaScript needs a value product.js
- TypeScript 5.9.3
product.js(6,27): error TS2693: 'Product' only refers to a type, but is being used as a value here. product.js(10,28): error TS2693: 'string' only refers to a type, but is being used as a value here. product.js(10,36): error TS1109: Expression expected.
- TypeScript 6.0.3same as 5.9.3
The same errors as 5.9.3.
- TypeScript 7.0.2differs from 5.9.3
product.js(10,36): error TS1109: Expression expected.
What works in every version: Check the shape with a type guard, and give the Set its type with @type.
The example A type guard for the check, and the type in a JSDoc tag on this page shows it, with the same result under every version.
Gotchas
- The reverse does not apply in a
.jsfile: a value's name used as a type in JSDoc, such as@type {defaults}, is read astypeof defaultsand not reported. Writingtypeofis still clearer. - With an old
lib,PromiseandSymbolare reported with a variant of this message that suggests changinglib(TS2585); see TS2583.
Sources
- Narrowing: Using type predicates · TypeScript handbook
- JSDoc Reference · TypeScript handbook
- diagnosticMessages.json (TypeScript 5.9.3) · TypeScript on GitHub
Last reviewed 20 October 2026.