In a .ts file, total!.textContent tells TypeScript that total is not null or undefined. In a .js file the ! operator is TS8013 ("Non-null assertions can only be used in TypeScript files"), and there is no JSDoc tag for it. The handbook also notes that ! inside a JSDoc type, as in {!HTMLElement}, has no meaning.
JavaScript code says the same thing in one of four ways, each shown in the second example: check and throw, skip with ?., call an assertion function, or cast.
At a glance
| Use | JSDoc in a .js file | TypeScript in a .ts file |
|---|---|---|
| Check and stop | if (!total) throw new Error("#total is missing"); | total! |
| Skip when missing | document.querySelector("#badge")?.classList.add("on"); | document.querySelector("#badge")?.classList.add("on"); |
| Assertion function | assert(count, "#count is missing"); | const count = document.querySelector("#count")!; |
| Cast | /** @type {HTMLElement} */ (document.querySelector("#note")) | document.querySelector("#note") as HTMLElement |
Examples
const total = document.querySelector("#total");
total!.textContent = "$39.00";error TS8013: Non-null assertions can only be used in TypeScript files.
export {};jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} /**
* @param {unknown} value
* @param {string} message
* @returns {asserts value}
*/
function assert(value, message) {
if (!value) throw new Error(message);
}
// 1. Check and stop: the rest of the code sees a non-null value.
const total = document.querySelector("#total");
if (!total) throw new Error("#total is missing");
total.textContent = "$39.00";
// 2. Optional chaining: do nothing when it is missing.
document.querySelector("#badge")?.classList.add("visible");
// 3. An assertion function.
const count = document.querySelector("#count");
assert(count, "#count is missing");
count.textContent = "2";
// 4. A cast: a claim TypeScript does not check.
const note = /** @type {HTMLElement} */ (document.querySelector("#note"));
note.hidden = false;
export {};const total = document.querySelector("#total");
total!.textContent = "$39.00";
document.querySelector("#badge")?.classList.add("visible");
const count = document.querySelector("#count")!;
count.textContent = "2";
const note = document.querySelector("#note") as HTMLElement;
note.hidden = false;
export {};jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} Which one to use
A check that throws is the most honest: it narrows the type and fails with a clear message at the point where the assumption breaks. An assertion function packages the same check for reuse. Optional chaining fits elements that may legitimately be absent.
A cast is the closest to ! and checks just as little: if the element is missing, the code fails later with a less helpful error. Keep casts for cases a check cannot express.
Gotchas
{!Type}in JSDoc is read asType; it does not removenullfrom the value assigned to it.- Optional chaining returns
undefinedwhen the value is missing, so its result may need its own check.
Questions
How do I write a non-null assertion in JSDoc?
There is no JSDoc form. Use a check (if (!el) throw …), an assertion function, optional chaining, or a cast to the non-null type.
Sources
- Everyday Types: non-null assertion operator · TypeScript handbook
- JSDoc Reference: unsupported patterns · TypeScript handbook
Last reviewed 20 October 2026.