Sometimes you know more about a value than the compiler can prove: the element returned by querySelector, the shape of data you just parsed, or a map entry you inserted a moment ago. A type assertion tells TypeScript to treat an expression as a specific type. This lesson explains the as syntax, the rules that limit it, the non-null operator !, and when an assertion is a mistake that hides a real bug.
as Syntaxconst el = document.getElementById("app") as HTMLDivElement;
el.style.padding = "1rem";
const data = JSON.parse('{"id": 1}') as { id: number };
console.log(data.id);Assertions exist only at compile time; they emit no JavaScript and perform no runtime check. If #app is actually a <span>, the code still runs and fails later. An assertion is a promise you make, not a conversion.
The older angle-bracket form <HTMLDivElement>el means the same thing, but it clashes with JSX and cannot be used in .tsx files. Prefer as everywhere for consistency.
TypeScript only permits an assertion when one type is assignable to the other in at least one direction (the types "sufficiently overlap"). This blocks obvious nonsense:
const s = "hello" as number;
// Error: Conversion of type 'string' to type 'number' may be a mistake
// because neither type sufficiently overlaps with the other.Narrowing a wide type to a narrower one is allowed (unknown to string, Element to HTMLInputElement), and so is widening. When you truly need to force unrelated types, route through unknown:
const n = "42" as unknown as number; // compiles; almost always a code smellReserve the double assertion for test fixtures and interop code, and add a comment explaining why.
!Under strictNullChecks, many APIs return T | undefined or T | null. The postfix ! removes null and undefined from a type:
const cache = new Map<string, number>();
cache.set("total", 10);
const total = cache.get("total")!; // number
const first = [3, 1, 2].find((x) => x > 2)!; // numberLike as, the ! is erased at compile time. If the value really is undefined, you get a runtime TypeError later instead of a compiler warning now. Prefer a real check when the value can legitimately be missing:
const value = cache.get("total");
if (value === undefined) throw new Error("total not initialised");
value.toFixed(2); // narrowed to number, no assertion neededA related ! appears on declarations. It tells the compiler a variable or class field will be assigned before use, even though the assignment happens somewhere it cannot see (for example in a lifecycle method or setup function):
let config!: { port: number };
initialise(); // assigns config
console.log(config.port); // no "used before being assigned" error
class Repo {
private db!: Database; // set in connect(), not in the constructor
}as constas const is an assertion that makes every literal keep its literal type and every property readonly. It is the standard way to build a typed lookup table or a union from an array:
const HTTP = { ok: 200, notFound: 404 } as const;
// { readonly ok: 200; readonly notFound: 404 }
const STATUSES = ["draft", "published"] as const;
type Status = (typeof STATUSES)[number]; // "draft" | "published"satisfies| Technique | Runtime check | Changes type | Use when |
|---|---|---|---|
| x as T | No | Yes, forcibly | Compiler lacks information you genuinely have |
| Type guard (typeof, instanceof, custom) | Yes | Yes, safely | The value could be several things at runtime |
| x satisfies T | No | No | You want validation without widening |
| x! | No | Removes null/undefined | You have just guaranteed the value exists |
The safest code narrows with a runtime check. Assertions are the tool of last resort for gaps in the compiler's knowledge, not a way to silence errors.
! after every .get() or .find() call. If a lookup can miss, handle it.as any to make an error disappear. The error was probably telling you something.What does a type assertion `value as T` do at runtime?
value as T overrides the compiler's view of a type; it is erased at compile time and performs no check.as unknown as T bypasses this and should be rare.! strips null and undefined; a real check is safer when a value can be missing.let x!: T and field!: T are definite assignment assertions for values initialised elsewhere.as const produces readonly literal types, ideal for deriving unions from arrays and objects.Next lesson: Null and Undefined: strictNullChecks, Optional Chaining and Nullish Coalescing — how TypeScript models missing values and the operators that handle them safely.