Suggest an editImprove this articleRefine the answer for “Type assertions in TypeScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Type assertions** tell TypeScript to treat a value as a specific type without changing it at runtime. ```typescript const input = document.getElementById('email') as HTMLInputElement; input.value = 'test@example.com'; // Non-null assertion strips null | undefined const root = document.getElementById('root')!; ``` **Key point:** assertions are erased at compile time - no runtime checks, no conversion.Shown above the full answer for quick recall.Answer (EN)Image**Type assertions** let you override TypeScript's inferred type for a value, telling the compiler "I know what this is" without changing anything at runtime. ## Theory ### TL;DR - Assertion is like handing a blurry photo to a developer and saying "treat this as a cat photo" - the compiler stops complaining, but if it is actually a dog, the crash happens at runtime - Two syntaxes: `value as Type` (works everywhere) and `<Type>value` (breaks in JSX, do not use in React) - No runtime code is emitted, no conversion happens, no check runs - Use when you have external proof of the type (docs, manual checks already done); use type guards otherwise - `as const` and `satisfies` are often the better choice over a plain assertion ### Quick example ```typescript const value: any = "hello world"; // Assertion tells the compiler: treat this as a string const length = (value as string).length; // 11 // Wrong type still crashes at runtime - compiler won't save you const wrong = ("hello" as unknown as number); console.log(typeof wrong); // "string" - still a string ``` The compiled JS for `value as string` is just `value`. The assertion disappears completely. ### Two syntaxes TypeScript supports two ways to write an assertion: ```typescript // as syntax - works everywhere, preferred const input = document.getElementById('email') as HTMLInputElement; // Angle-bracket syntax - fails in JSX, parses as an element tag const input2 = <HTMLInputElement>document.getElementById('email'); ``` In React TSX files, always use `as`. The angle-bracket form triggers a JSX parse error. ### Key difference from type casting Type assertions are compile-time only. Unlike Java or C# where casting actually converts the value, TypeScript erases the assertion and emits nothing: ```typescript const value: any = "42"; const num = value as number; // Compiles fine console.log(num * 2); // NaN - still a string at runtime ``` No V8 involvement, no runtime check, no conversion. Pure build-time hint. ### When to use - **DOM elements with known ID**: `document.getElementById('email') as HTMLInputElement` - you know the element type, TypeScript does not - **JSON.parse output**: parse to `unknown`, validate the shape, then assert to your interface - **Third-party libs with loose typings**: assert after you have verified the shape yourself - **Union types already narrowed manually**: assertion instead of an extra type guard in performance-sensitive paths Avoid asserting on values you have not verified at runtime. When unsure, a type guard is safer. ### Non-null assertion The `!` operator is a shorthand for "this is not null or undefined": ```typescript // TypeScript sees: HTMLElement | null const element = document.getElementById('root'); // Non-null assertion - you are sure it exists element!.innerHTML = 'Hello'; ``` This is the most common source of runtime crashes in TypeScript codebases. Use it only when you can guarantee the value is present, and prefer an `if` check when there is any doubt. ### as const `as const` is different from a regular assertion. It freezes a value to its exact literal type and marks everything readonly: ```typescript // Without as const - TypeScript widens to string[] const colors = ['red', 'green', 'blue']; // string[] // With as const - exact readonly tuple const colorsConst = ['red', 'green', 'blue'] as const; // readonly ['red', 'green', 'blue'] type Color = typeof colorsConst[number]; // 'red' | 'green' | 'blue' ``` This pattern is standard for deriving union types from arrays of constants. ### satisfies vs assertions (TypeScript 4.9+) `satisfies` checks that a value matches a type but keeps the narrowest inferred type per property. A regular assertion or annotation widens everything: ```typescript type Colors = 'red' | 'green' | 'blue'; // With type annotation - individual types are lost const palette: Record<Colors, string | number[]> = { red: [255, 0, 0], green: '#00ff00', blue: [0, 0, 255] }; palette.red; // string | number[] - lost precision // With satisfies - checks compatibility, keeps exact types const palette2 = { red: [255, 0, 0], green: '#00ff00', blue: [0, 0, 255] } satisfies Record<Colors, string | number[]>; palette2.red; // number[] palette2.green; // string ``` On TypeScript 4.9+, `satisfies` is usually the better choice for object typing. ### Double assertions When TypeScript blocks an assertion between incompatible types, you can chain through `unknown`: ```typescript const value: string = "hello"; // Error: no overlap between string and number // const num = value as number; // Double assertion - compiles, but almost always wrong const num = value as unknown as number; ``` Double assertions signal a design problem. Treat them as a reason to revisit the type structure, not a shortcut. ### Common mistakes **Asserting without a null check:** ```typescript // You assume the element exists - it might not const input = document.getElementById('email') as HTMLInputElement; input.value = 'test'; // Runtime crash if element is absent // Better const input = document.getElementById('email'); if (input instanceof HTMLInputElement) { input.value = 'test'; } ``` **Angle-bracket syntax in TSX:** ```tsx // Parse error - React sees a JSX element const el = <HTMLInputElement>document.getElementById('foo'); // Correct const el = document.getElementById('foo') as HTMLInputElement; ``` **Asserting to a structurally incomplete type:** ```typescript interface User { name: string; age: number; } const obj = { name: 'Alice' } as User; console.log(obj.age); // undefined - TypeScript let this through ``` Structural typing allows missing properties when you assert. Validate before asserting. **Using `as any` to suppress real errors:** ```typescript // Bad - all checks are off function processData(data: ComplexType) { return (data as any).someMethod(); } // Better - check first function processData(data: ComplexType) { if ('someMethod' in data && typeof (data as any).someMethod === 'function') { return (data as { someMethod: () => unknown }).someMethod(); } } ``` ### Real-world usage - **React**: `useRef<HTMLElement>(null)` then `ref.current as HTMLElement` inside event handlers - **Express**: `req.body as { userId: number }` after `express.json()` middleware has already validated the structure - **Next.js**: asserting `getServerSideProps` return values to page prop shapes - **Redux Toolkit**: `payload as SpecificPayload` inside discriminated union action handlers - **JSON.parse**: parse to `unknown`, validate with `in` checks, then assert to the interface ### Follow-up questions **Q:** What is the difference between `as` and angle-bracket syntax? **A:** They do the same thing. But `<T>expr` fails in JSX because the parser reads it as an element tag. Use `as` in all React and TSX files. **Q:** Does a type assertion add any runtime code? **A:** No. TypeScript erases assertions completely during compilation. The JS output is identical with and without the assertion. **Q:** When should you use a type guard instead of an assertion? **A:** When you do not have external proof of the type. A type guard (`typeof`, `instanceof`, `in`) narrows the type safely with an actual runtime check. An assertion skips that check and trusts you. **Q:** What is `as const` and how is it different from a regular assertion? **A:** A regular assertion narrows to a named type. `as const` freezes the value to its exact literal type and marks everything readonly. It is used to derive precise union types from arrays or lock down config objects. **Q:** Why does `satisfies` often beat assertions for object typing? **A:** Assertions drop type inference. `satisfies` checks that your object matches a type but keeps the narrowest inferred type per property. `const config = { timeout: 5000 } satisfies Config` keeps `config.timeout` as `number`, not `Config`. An assertion would widen it. **Q:** You have `JSON.parse(rawString)`. How do you type the result safely? **A:** Parse to `unknown`, then use `in` checks or a type guard to validate the shape before asserting. Asserting directly from `JSON.parse` without validation is a runtime crash waiting to happen - the schema might not match the actual data. ## Examples ### Basic: extracting length from any ```typescript const data: any = "TypeScript"; const strLength = (data as string).length; console.log(strLength); // 10 ``` You tell the compiler `data` is a string, so `.length` is valid. At runtime, it already is a string, so no crash. If `data` were `42`, you would get `undefined` because numbers do not have `.length`. ### Intermediate: DOM input in a form handler ```typescript const handleSubmit = (e: Event) => { e.preventDefault(); const emailInput = document.getElementById('email') as HTMLInputElement; if (!emailInput) return; // null check before use const email = emailInput.value; console.log('Submitting:', email); }; document.getElementById('form')?.addEventListener('submit', handleSubmit); ``` `getElementById` returns `HTMLElement | null`. The assertion to `HTMLInputElement` gives you `.value` and other input-specific properties. The null check before use prevents a runtime crash. ### Advanced: satisfies vs assertion for config objects ```typescript type Env = 'development' | 'production' | 'test'; interface AppConfig { env: Env; apiUrl: string; timeout: number; } // With type annotation - exact literals are widened const configA = { env: 'production', apiUrl: 'https://api.example.com', timeout: 5000 } as AppConfig; configA.env; // Env - widened to the full union // With satisfies - checks shape, keeps literals const configB = { env: 'production', apiUrl: 'https://api.example.com', timeout: 5000 } satisfies AppConfig; configB.env; // 'production' - exact literal preserved ``` I have seen codebases where switching from `as AppConfig` to `satisfies AppConfig` immediately caught missing required fields that the assertion had quietly let through. In production config files, `satisfies` gives you both the type check and precise inference.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.