Type expression

any and unknown

  • Basics
  • Tested with TypeScript 5.9

Both types accept every value. The difference is what you may do with it afterwards. With any, TypeScript allows everything, so a misspelled property or a wrong method call passes silently. With unknown, TypeScript allows nothing until you narrow the value with typeof, instanceof, in or a type guard.

Use unknown for values whose shape you have not checked yet: parsed JSON, postMessage data, caught errors. TypeScript added it in version 3.0 as the safe counterpart to any.

At a glance

Use JSDoc in a .js file TypeScript in a .ts file
Anything, checked before use /** @type {unknown} */ : unknown
Anything, never checked /** @type {any} */ : any
Closure spellings of any /** @type {*} */ or /** @type {?} */ : any

Examples

settings.js settings.ts TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
/**
 * @param {string} text
 * @returns {{ theme: string }}
 */
export function readSettings(text) {
  /** @type {unknown} */
  const data = JSON.parse(text);
  if (typeof data === "object" && data !== null && "theme" in data && typeof data.theme === "string") {
    return { theme: data.theme };
  }
  return { theme: "light" };
}

readSettings('{"theme":"dark"}');
export function readSettings(text: string): { theme: string } {
  const data: unknown = JSON.parse(text);
  if (typeof data === "object" && data !== null && "theme" in data && typeof data.theme === "string") {
    return { theme: data.theme };
  }
  return { theme: "light" };
}

readSettings('{"theme":"dark"}');
unknown makes you check a value before using it.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
settings.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: settings.js(6,10): error TS18046: 'data' is of type 'unknown'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. settings.js(6,10): error TS18046: 'data' is of type 'unknown'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. settings.js(6,10): error TS18046: 'data' is of type 'unknown'. checked 21 October 2026
/** @param {string} text */
export function themeOf(text) {
  /** @type {unknown} */
  const data = JSON.parse(text);
  return data.theme;error TS18046: 'data' is of type 'unknown'.
}
Using an unknown value without a check.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
settings.js TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
/** @param {string} text */
export function themeOf(text) {
  /** @type {any} */
  const data = JSON.parse(text);
  // No error: with any, the misspelled property and the wrong method both pass.
  return data.thmee.toUpperCas();
}

/** @type {*} */
const alsoAny = 42;
alsoAny.push("x");
any switches checking off, typos included.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

{*} and {?}

The Closure Compiler spellings {*} and {?} both mean any to TypeScript. They turn up in older JSDoc; {*} in particular is often meant as "anything" in the sense of unknown. Rewrite them as unknown when you want the check.

Where any comes from without asking

In a .js file, several things are any unless you type them: untyped parameters (reported as TS7006 under strict), JSON.parse results, empty array literals and new Map() without type arguments. Give the JSON.parse result the type unknown right away, as the first example does, and narrow it before use.

Gotchas

  • any spreads: a property read from an any value is any too, and so is the result of calling it.
  • Under strict, a catch variable is already unknown.
  • unknown in a union swallows the other members: string | unknown is just unknown.

Questions

Should I use any or unknown in JSDoc?

unknown, unless you are deliberately switching checking off for a value. unknown keeps the checks and makes the narrowing explicit.

Sources

Last reviewed 20 October 2026.