🌟 The Challenge: Bridging Type Safety Between DB and App Layers
In many real-world applications, the database schema often permits null
values — for good reason. For instance, a date
field in an event table might be intentionally left blank if the event date hasn’t been finalized yet.
However, your application layer — especially the part that renders UI — usually prefers clean, reliable data. If a date is null
, you probably don’t want to show that event in the UI at all.
This creates a gap between what your database allows and what your application expects.
You might be tempted to solve this quickly with a forced type cast like:
let x: unknown = 'hello';
console.log((x as string).length);
But relying on type assertions skips safety checks and can lead to runtime bugs — exactly what TypeScript is meant to help you avoid.
🎯 The Solution: Using Type Guards
Instead of casting, a safer and cleaner solution is to use Type Guards — a TypeScript feature that allows you to narrow types in a type-safe way.
Let’s say your application pulls in event data like this:
// event.dto.ts - from database or external API
type EventDto = {
name: string;
date: Date | null;
};
But your UI layer expects all events to have a valid date
:
// event.ts - for use within the app
type Event = {
name: string;
date: Date;
};
To filter out incomplete events, define a custom type guard:
function hasDate(event: EventDto): event is Event {
return event.date !== null;
}
Then you can filter safely and TypeScript will infer the correct type:
export function filterValidEvents(events: EventDto[]): Event[] {
return events.filter(hasDate);
}
Now your UI layer will only receive events that have confirmed dates — and TypeScript will enforce this guarantee for you.
✅ Benefits
Type safety without unnecessary assertions.
Separation of concerns: the DB layer can remain flexible, while the app layer stays strict.
Scalability: you can extend this pattern to other nullable fields and DTOs.
🧠Final Thoughts
Dealing with partial or nullable data from external sources is inevitable. But with TypeScript’s type guards, you don’t have to compromise safety or clarity.
Embrace the gap between layers — and bridge it elegantly.
Top comments (0)