TypeScript was created for one purpose: to bring safety and clarity to JavaScript. When Microsoft first released it, their mission was simple - give developers predictable types, reduce runtime bugs, and make debugging easier.
But even with all the guardrails TypeScript provides, there’s one tiny operator that many developers misuse without realizing the long-term cost:
? — the optional modifier.
It looks harmless. It’s convenient. But using it in the wrong places quietly destroys the type safety TypeScript was built for.
Let’s talk about why — with real-world examples, better patterns, and official TS-backed solutions.
Where Things Start to Go Wrong
Imagine a simple model:
type User = {
id: string;
name: string;
email: string;
};
A User always has an ID.
This is part of your domain logic. The backend guarantees it.
But when you’re building a form to create a user, you don’t have an id yet. And this is where many developers introduce a subtle bug:
id?: string;
Looks tiny, but this changes everything. Because now, TypeScript thinks:
- A user might have an ID
- Or might not
- Anywhere in the entire codebase
Your core type becomes weaker, and the compiler loses its ability to actually protect you.
Real Life Example: When TS Yells at You
Let me show you how this problem shows up at work. I had code like this:
const customerId = client.customerId;
const updatedData = await updateCustomerById({ id: customerId, body: data }).unwrap();
Nothing looks wrong…
But TypeScript hits you with this:
'client' is possibly 'undefined'.ts(*****)
Why?
Because somewhere in the type definitions, someone got lazy and wrote:
type Client = {
customerId?: number;
...
};
That one little ? forces TypeScript to assume:
-
client.customerIdmight be undefined - and you must guard every line of code
So you end up writing completely unnecessary defensive code:
if (!client?.id) throw new Error("Client id missing!");
// or maybe, you add condition in submit function
The real problem is the type design, not the logic.
The Fix the TypeScript Docs Recommend
TypeScript’s official documentation gives us tools for this exact situation.
TypeScript provides several utility types to facilitate common type transformations. These utilities are available globally. [TS docs]
The solution is not to make everything optional.
The solution is to derive specific types for specific contexts using utility types.
Use Omit — Not ?
If a User always has an ID, but your form doesn’t, you create a separate type:
type CreateUserInput = Omit<User, "id">;
Done.
Clean.
Safe.
Predictable.
Your domain stays strict:
type User = {
id: number;
name: string;
email: string;
};
This is exactly what TypeScript recommends in its documentation on building new types from existing ones.
Final Thoughts
The next time you feel the urge to sprinkle ? everywhere to make TS stop complaining, pause a moment and ask:
“Am I modeling reality… or am I just silencing TypeScript?”
Use Omit, Pick, and TypeScript’s built-in utilities. Your future self — and TypeScript — will thank you. Explore more and comment below how utility types saved you!
Top comments (0)