Skip to main content

Practical Topics

Type Assertions vs Type Casting​

Type Assertions in TypeScript​

  • A type assertion tells the TypeScript compiler: “I know more about this value’s type than you do.”
  • It doesn’t change the actual runtime value — it’s only a compile-time hint.

There are two ways to write type assertions:

// Angle-bracket syntax
let value: unknown = "hello";
let strLength = (<string>value).length;

// `as` syntax (preferred in modern TS)
let strLength2 = (value as string).length;

Both mean: “Treat value as a string.”

Example

type Person = {
name: string;
age: number;
};

const person = {} as Person;
person.name = "Alice";
person.age = 25;
  • The empty object {} doesn’t match Person.
  • But as Person tells TypeScript: “trust me, this is a Person.”
  • Dangerous if misused:
const person = {} as Person;
console.log(person.age.toFixed()); // Runtime error (age is undefined)

Type assertions don’t do runtime checks. They only silence the compiler.

Type Casting​

In JavaScript, type casting usually means converting values from one type to another at runtime, e.g.:

let num = Number("123"); // string → number
let str = String(123); // number → string
let bool = Boolean(1); // number → boolean

This is runtime conversion, not just a compile-time hint.

const str = "123";
const num: number = Number(str); // runtime conversion
  • "123" is actually converted into 123.
  • If you pass "abc", Number("abc") → NaN.

Differences between Type Casting and Type Assertion​

FeatureType Assertion (as, <T>)Type Casting (runtime conversion)
When it happensCompile-time onlyRuntime (JS execution)
EffectJust changes how TS checks typesActually changes the data value
SafetyCan be unsafe if misusedSafe but may fail (e.g., Number("abc") → NaN)
Example(value as string).lengthNumber("123") → 123
  • Use type assertions sparingly, only when you’re sure about the type.
  • Use type casting (conversion functions) when you really need to change the data type.
  • Avoid overusing assertions like as any → defeats the purpose of TypeScript.

Non-null Assertion Operator !​

The non-null assertion operator (!) tells TypeScript:

“I’m sure this value is not null or undefined, trust me.”

It doesn’t do anything at runtime — it’s only a compile-time hint to the TypeScript type checker.

Why Do We Need It?​

TypeScript has strict null checks (strictNullChecks: true in tsconfig.json), meaning:

let value: string | null = null;

// Error ❌: value might be null
console.log(value.length);

The compiler complains because value could be null. But sometimes, you know for sure that it won’t be null at runtime. That’s when you use !.

let value: string | null = "Hello";

// Tell TypeScript: value is definitely not null
console.log(value!.length); // ✅ OK
Here:
  • Without !, TS warns that value might be null.
  • With !, TS trusts you.

Strict Mode (strictNullChecks, etc.)​

Strict mode is a set of compiler options that make TypeScript more type-safe and strict.

You enable it in your tsconfig.json:

{
"compilerOptions": {
"strict": true
}
}

strict: true is shorthand for turning on all strict type-checking options.

strictNullChecks​

null and undefined are not assignable to other types unless explicitly allowed.

let name: string = "Alice";
name = null; // ❌ Error
name = undefined; // ❌ Error

To allow null/undefined, you must declare them explicitly:

let name: string | null = null; // ✅

This avoids runtime errors like: Cannot read property 'length' of null.

strictPropertyInitialization​

Ensures class properties are initialized before use.

class Person {
name: string; // ❌ Error: Property 'name' has no initializer
}

Fix 1: Initialize directly

class Person {
name: string = "Default";
}

Fix 2: Initialize in constructor

class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}

Fix 3: Use definite assignment assertion (!)

class Person {
name!: string; // tells TS: "I will assign this before use"
}

noImplicitAny​

Disallows variables/functions from having an implicit any type.

function greet(message) {
// ❌ Error: message implicitly has type 'any'
console.log(message);
}

Fix: Add explicit type

function greet(message: string) {
console.log(message);
}

This prevents TypeScript from silently defaulting to any.

strictFunctionTypes​

Makes function parameter types checked more strictly, ensuring safe assignments.

type Fn = (x: number) => void;

let f1: Fn;
let f2 = (x: number | string) => {};

f1 = f2; // ❌ Error in strict mode

Prevents unsafe function assignments.

strictBindCallApply​

Ensures the methods .bind, .call, and .apply are type-safe.

function greet(name: string) {
return `Hello ${name}`;
}

greet.call(null, "Alice"); // ✅
greet.call(null, 123); // ❌ Error

Without this, .call could accept wrong arguments silently.

alwaysStrict​

Ensures that every file is parsed in ECMAScript strict mode ("use strict";).

// with alwaysStrict: true
"use strict"; // enforced automatically

This matches modern JavaScript behavior and avoids subtle bugs.