Skip to main content
Practice Problems

Type assertions in TypeScript

What are Type Assertions?

Type Assertions are a way to tell the TypeScript compiler: "I know the type of this value better than you do". This is not type casting and doesn't change the value at runtime — it's only a hint for the compiler.


Syntax

TypeScript supports two syntaxes for type assertions:

as Syntax

typescript
const value: any = "hello"; const length = (value as string).length; // 5

Angle-bracket Syntax

typescript
const value: any = "hello"; const length = (<string>value).length; // 5

Important:

In JSX/TSX, only as syntax can be used, as angle brackets conflict with JSX syntax.


When to Use Type Assertions?

Working with DOM

typescript
// TypeScript doesn't know which element will be returned const input = document.getElementById('email'); // HTMLElement | null // Assert specific type const emailInput = document.getElementById('email') as HTMLInputElement; emailInput.value = 'test@example.com'; // Alternative const emailInput2 = <HTMLInputElement>document.getElementById('email');

Working with API and any type

typescript
const response: any = await fetch('/api/user').then(r => r.json()); interface User { id: number; name: string; email: string; } const user = response as User; console.log(user.name);

Narrowing union types

typescript
type Result = { success: true; data: string } | { success: false; error: string }; function handleResult(result: Result) { if (result.success) { // TypeScript knows it's { success: true; data: string } console.log(result.data); } else { // TypeScript knows it's { success: false; error: string } console.log(result.error); } } // But sometimes explicit assertion is needed: const result = getResult(); const successResult = result as { success: true; data: string };

Working with literals

typescript
// TypeScript infers type as string const method = 'GET'; // string // Need specific literal const method2 = 'GET' as const; // 'GET' // Or const method3 = 'GET' as 'GET' | 'POST';

Type Assertions vs Type Casting

Type Assertions (TypeScript)

typescript
const value: any = "42"; const num = value as number; // Compiles, but runtime error! console.log(num * 2); // NaN (string multiplied by number)

Type Casting (other languages)

javascript
// In other languages, casting converts the value String num = "42"; int converted = (int) num; // Actually converts string to number

Important:

Type Assertions don't perform conversion and don't check type at runtime. It's only a hint for the compiler.


Double Assertions

Sometimes TypeScript doesn't allow direct assertion between incompatible types:

typescript
const value: string = "hello"; // Error: Conversion of type 'string' to type 'number' may be a mistake // const num = value as number; // Double assertion through unknown (or any) const num = value as unknown as number;

Caution:

Double assertions are a sign of typing issues. Use only as last resort.


Non-null Assertion Operator

The ! operator tells TypeScript that a value is definitely not null or undefined.

Syntax

typescript
// TypeScript thinks it can be null const element = document.getElementById('root'); // HTMLElement | null // We're sure element exists const elementNonNull = document.getElementById('root')!; // HTMLElement elementNonNull.innerHTML = 'Hello';

Usage Examples

typescript
interface User { name: string; email?: string; } const user: User = { name: 'John', email: 'john@example.com' }; // Error: Object is possibly 'undefined' // const emailLength = user.email.length; // With check if (user.email) { const emailLength = user.email.length; } // With non-null assertion (if sure) const emailLength = user.email!.length;

With Optional Chaining

typescript
interface Config { api?: { url?: string; }; } const config: Config = { api: { url: 'https://api.example.com' } }; // Without non-null assertion const url = config.api?.url; // string | undefined // With non-null assertion const urlNonNull = config.api!.url!; // string

Caution:

Non-null assertion bypasses TypeScript checks. If value turns out to be null or undefined, you'll get a runtime error.


Const Assertions

as const creates readonly literal types.

Primitives

typescript
// Regular declaration let x = 'hello'; // string // With as const let y = 'hello' as const; // 'hello' (literal type)

Objects

typescript
// Regular object const config = { host: 'localhost', port: 3000 }; // { host: string; port: number; } // With as const const configConst = { host: 'localhost', port: 3000 } as const; // { readonly host: 'localhost'; readonly port: 3000; } // Cannot modify // configConst.port = 8080; // Error

Arrays

typescript
// Regular array const colors = ['red', 'green', 'blue']; // string[] // With as const const colorsConst = ['red', 'green', 'blue'] as const; // readonly ['red', 'green', 'blue'] type Color = typeof colorsConst[number]; // 'red' | 'green' | 'blue'

Practical Application

typescript
// Creating union type from array const ROLES = ['admin', 'user', 'guest'] as const; type Role = typeof ROLES[number]; // 'admin' | 'user' | 'guest' function checkRole(role: Role) { // ... } checkRole('admin'); // OK // checkRole('moderator'); // Error

Satisfies Operator (TypeScript 4.9+)

satisfies checks type conformance while preserving type inference.

typescript
type Colors = 'red' | 'green' | 'blue'; const colors = { red: [255, 0, 0], green: '#00ff00', blue: [0, 0, 255] } satisfies Record<Colors, string | number[]>; // TypeScript preserved exact types colors.red; // number[] colors.green; // string colors.blue; // number[] // If we used type assertion: const colors2: Record<Colors, string | number[]> = { red: [255, 0, 0], green: '#00ff00', blue: [0, 0, 255] }; colors2.red; // string | number[] (lost precision)

When NOT to Use Type Assertions

Instead of Proper Typing

typescript
// Bad function getUser() { return { id: 1, name: 'John' } as any; } // Good interface User { id: number; name: string; } function getUser(): User { return { id: 1, name: 'John' }; }

To Fix Typing Errors

typescript
// Bad - hiding the problem const value: number = "hello" as any as number; // Good - fix the problem const value: string = "hello"; const num: number = parseInt(value, 10);

When Type Guards Can Be Used

typescript
function processValue(value: string | number) { // Bad const str = value as string; return str.toUpperCase(); // Good if (typeof value === 'string') { return value.toUpperCase(); } return value.toString(); }

Safe Alternatives

Type Guards

typescript
function isString(value: any): value is string { return typeof value === 'string'; } const value: any = "hello"; if (isString(value)) { // TypeScript knows value is string console.log(value.toUpperCase()); }

Discriminated Unions

typescript
type Shape = | { kind: 'circle'; radius: number } | { kind: 'square'; size: number }; function getArea(shape: Shape) { switch (shape.kind) { case 'circle': return Math.PI * shape.radius ** 2; case 'square': return shape.size ** 2; } }

Optional Chaining

typescript
// Instead of non-null assertion const value = obj.prop!.nested!.value; // Use optional chaining const value = obj.prop?.nested?.value; // string | undefined

Common Mistakes

Asserting Incompatible Types

typescript
const num = 42; // Error: cannot convert number to string // const str = num as string; // Need double assertion (but it's bad!) const str = num as unknown as string;

Ignoring Errors via as any

typescript
// Bad function processData(data: ComplexType) { return (data as any).someMethod(); } // Good function processData(data: ComplexType) { if ('someMethod' in data && typeof data.someMethod === 'function') { return data.someMethod(); } }

Loss of Type Safety

typescript
// Bad - losing type safety const users = getUsers() as any[]; // Good interface User { id: number; name: string; } const users = getUsers() as User[];

Practical Patterns

Safe DOM Extraction

typescript
function getElement<T extends HTMLElement>(id: string): T | null { return document.getElementById(id) as T | null; } const input = getElement<HTMLInputElement>('email'); if (input) { input.value = 'test'; }

Working with unknown

typescript
function parseJSON(json: string): unknown { return JSON.parse(json); } const data = parseJSON('{"name": "John"}'); // Type guard for safety function isUser(data: unknown): data is { name: string } { return typeof data === 'object' && data !== null && 'name' in data && typeof (data as any).name === 'string'; } if (isUser(data)) { console.log(data.name); }

Conclusion

Type Assertions:

  • Don't convert values, only specify type to compiler
  • Syntax: as (preferred) or <> (not in JSX)
  • Useful when working with DOM and any
  • Non-null assertion ! removes null | undefined
  • as const creates readonly literals
  • satisfies (4.9+) checks type while preserving inference
  • Use cautiously — can hide problems

In Interviews:

Important to be able to:

  • Explain difference between type assertions and type casting
  • Show both syntaxes (as and <>)
  • Explain when to use as const
  • Discuss non-null assertion operator
  • Provide examples of safe alternatives (type guards)
  • Explain risks of excessive assertion use

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems