Unlike Kotlin where object.someValueThatCouldBeNull!! would assert that someValueThatCouldBeNull is NOT null (and throw if it is), in TypeScript the object.someValueThatCouldBeNull! operator tells the compiler:

“Don’t worry about this, look away, even if you know this is null, I don’t care, let me be”.

In other words, even if object.someValueThatCouldBeNull is in fact NULL, object.someValueThatCouldBeNull! makes things compile as if it wasn’t NULL - provides NO runtime protection.

Hence, instead of ! operator, it’s better to use a helper function like:

export function ensure<T>(value: T | null | undefined, name?: string): T {
  if (value === undefined) {
    throw new Error(`${name ?? 'Value'} is required but was undefined`);
  }
  if (value === null) {
    throw new Error(`${name ?? 'Value'} is required but was null`);
  }
  return value;
}
GT-Sandbox-Snapshot

Code

import { describe, it, expect, beforeEach, beforeAll } from 'vitest';
 
describe('Non-null assertion operator (!)', () => {
  it('does NOT throw when value is null - crashes at property access instead', () => {
    const data: { value: string | null } = {value: null};
 
    // This does NOT throw - the ! operator is compile-time only
    const assertedValue = data.value!;
 
    // assertedValue is still null at runtime
    expect(assertedValue).toBe(null);
 
    // The error happens when you try to USE the null value
    expect(() => {
      // This is where it actually crashes
      const length = data.value!.length;
    }).toThrow('Cannot read properties of null');
  });
 
  it('does NOT throw when value is undefined either', () => {
    const data: { value?: string } = {};
 
    // Again, this doesn't throw
    const assertedValue = data.value!;
 
    // It's still undefined
    expect(assertedValue).toBe(undefined);
 
    // Crashes when accessing properties
    expect(() => {
      const length = data.value!.length;
    }).toThrow('Cannot read properties of undefined');
  });
 
  it('ensure() helper DOES throw immediately', () => {
    function ensure<T>(value: T | null | undefined, name?: string): T {
      if (value == null) throw new Error(`${name ?? 'Value'} is required`);
      return value;
    }
 
    const data: { value: string | null } = {value: null};
 
    // This throws immediately, before any property access
    expect(() => {
      const safeValue = ensure(data.value, 'data.value');
    }).toThrow('data.value is required');
  });
});

Command to reproduce:

gt.sandbox.checkout.commit 9f402a4c847a161d5d6a \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./run.sh"

Recorded output of command:

 
up to date, audited 179 packages in 1s
 
33 packages are looking for funding
  run `npm fund` for details
 
2 moderate severity vulnerabilities
 
To address all issues (including breaking changes), run:
  npm audit fix --force
 
Run `npm audit` for details.
 
> glassthought-sandbox@1.0.0 test
> vitest run
 
 
 RUN  v0.34.6 /home/nickolaykondratyev/git_repos/glassthought-sandbox
 
 ✓ src/main.test.ts  (3 tests) 2ms
 
 Test Files  1 passed (1)
      Tests  3 passed (3)
   Start at  11:56:55
   Duration  484ms (transform 34ms, setup 0ms, collect 11ms, tests 2ms, environment 0ms, prepare 68ms)