DEV Community

Cover image for 🚀 Type Safety Across Layers: From Messy DBs to Clean Apps
Toshiya Matsumoto
Toshiya Matsumoto

Posted on

🚀 Type Safety Across Layers: From Messy DBs to Clean Apps

Every developer knows this pain: your database says “nullable is fine,” but your app screams “absolutely not.”

👉 Example: An event table with a date column. The DB allows null (because some events don’t have confirmed dates yet).
But in your React UI? Showing an event with date: null is not only useless — it’s a bug waiting to happen.

This mismatch creates an annoying gap between your DB schema and your application layer. Ignore it, and you’ll end up patching with type casts that look fine… until runtime crashes say otherwise.

⚠️ The Wrong Way: Casting Your Troubles Away

We’ve all done this:

let x: unknown = 'hello';
console.log((x as string).length);
Enter fullscreen mode Exit fullscreen mode

Sure, TypeScript stops complaining. But you’ve effectively turned off its seatbelt.
Type assertions bypass safety checks, leaving you with runtime landmines — the exact opposite of why you chose TypeScript in the first place.

✅ The Right Way: Type Guards

Enter Type Guards — TypeScript’s built-in way to narrow types safely.

Your DB or API DTO might look like this:

// event.dto.ts
type EventDto = {
  name: string;
  date: Date | null;
};
Enter fullscreen mode Exit fullscreen mode

But your app layer needs stricter guarantees:

// event.ts
type Event = {
  name: string;
  date: Date;
};
Enter fullscreen mode Exit fullscreen mode

Here’s how you bridge that gap:

function hasDate(event: EventDto): event is Event {
  return event.date !== null;
}

export function filterValidEvents(events: EventDto[]): Event[] {
  return events.filter(hasDate); <== here you go.
}
Enter fullscreen mode Exit fullscreen mode

Now, your UI only ever sees events with valid dates. TypeScript enforces the rule, no assertions required.

🎁 Why This Pattern Wins

Type Safety: no unsafe casts, no silent failures.

Separation of Concerns: DBs stay flexible; apps stay strict.

Scalable: extendable to any nullable fields or DTOs.

🧩 Takeaway

Nullable data is inevitable when dealing with databases or APIs.
But instead of brute-forcing types, let Type Guards do the heavy lifting.

They let you:

Accept messy, real-world data from your DB.

Hand your app layer clean, reliable objects.

Keep TypeScript as your safety net, not your afterthought.

👉 Next time you hit the null wall, don’t cast — guard.

Top comments (0)