Type expression

Type casts

  • Importing and casting
  • Since TypeScript 2.5

A @type comment directly before a parenthesized expression changes the expression's type: /** @type {HTMLInputElement} */ (document.querySelector("#email")) is the JavaScript spelling of document.querySelector("#email") as HTMLInputElement. TypeScript 2.5 added the form.

Both parts are required. Without the parentheses the comment is just a comment, and the value keeps its own type, Element | null, as the second example shows with two errors on the next line.

At a glance

Use JSDoc in a .js file TypeScript in a .ts file
A cast /** @type {HTMLInputElement} */ (document.querySelector("#email")) document.querySelector("#email") as HTMLInputElement
Through unknown /** @type {Target} */ (/** @type {unknown} */ (value)) value as unknown as Target

Examples

signup.js signup.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
const email = /** @type {HTMLInputElement} */ (document.querySelector("#email"));
const form = /** @type {HTMLFormElement} */ (document.getElementById("signup"));

form.addEventListener("submit", (event) => {
  if (!email.value.includes("@")) {
    event.preventDefault();
    email.setCustomValidity("Enter an email address");
    email.reportValidity();
  }
});

export {};
const email = document.querySelector("#email") as HTMLInputElement;
const form = document.getElementById("signup") as HTMLFormElement;

form.addEventListener("submit", (event) => {
  if (!email.value.includes("@")) {
    event.preventDefault();
    email.setCustomValidity("Enter an email address");
    email.reportValidity();
  }
});

export {};
Casting with @type and parentheses.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
search.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: search.js(4,1): error TS18047: 'field' is possibly 'null'. search.js(4,7): error TS2339: Property 'value' does not exist on type 'Element'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. search.js(4,1): error TS18047: 'field' is possibly 'null'. search.js(4,7): error TS2339: Property 'value' does not exist on type 'Element'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. search.js(4,1): error TS18047: 'field' is possibly 'null'. search.js(4,7): error TS2339: Property 'value' does not exist on type 'Element'. checked 21 October 2026
const field = /** @type {HTMLInputElement} */ document.querySelector("#q");

field.value = "";error TS18047: 'field' is possibly 'null'.error TS2339: Property 'value' does not exist on type 'Element'.

export {};
Without the parentheses there is no cast.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
quantity.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: quantity.js(4,29): error TS2352: Conversion of type 'string' to type 'number' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. quantity.js(4,29): error TS2352: Conversion of type 'string' to type 'number' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. quantity.js(4,29): error TS2352: Conversion of type 'string' to type 'number' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. checked 21 October 2026
const fromForm = "5";

const quantity = /** @type {number} */ (fromForm);error TS2352: Conversion of type 'string' to type 'number' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.

const parsed = Number(fromForm);

export { quantity, parsed };
A cast between unrelated types is rejected.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
search.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: search.js(2,47): error TS8016: Type assertion expressions can only be used in TypeScript files. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. search.js(2,47): error TS8016: Type assertion expressions can only be used in TypeScript files. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. search.js(2,47): error TS8016: Type assertion expressions can only be used in TypeScript files. checked 21 October 2026
const field = document.querySelector("#q") as HTMLInputElement;error TS8016: Type assertion expressions can only be used in TypeScript files.

export { field };
as is TypeScript syntax.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

What a cast checks

Very little. TypeScript only rejects casts between types that do not overlap at all, such as a string to a number (TS2352, "Conversion of type 'string' to type 'number' may be a mistake…"). Anything plausible passes: a cast to HTMLInputElement is accepted even if the selector matches a <div>, or nothing.

For data from outside (JSON, form values, postMessage), prefer a check that narrows, a type predicate or an assertion function, so a wrong assumption fails at once, with a clear message, instead of somewhere later.

as is TypeScript syntax

value as Type is an error in a .js file (TS8016, "Type assertion expressions can only be used in TypeScript files"), as the last example shows.

Gotchas

  • After running a formatter over a file full of casts, check that the parentheses survived; Prettier, for one, keeps them when a type comment is attached.
  • To cast between unrelated types on purpose, go through unknown first, as the TS2352 message suggests.

Questions

How do I cast a type in JavaScript with JSDoc?

Put /** @type {Type} */ before the expression and wrap the expression in parentheses: /** @type {Type} */ (expression).

Sources

Last reviewed 20 October 2026.