Hey there, fellow coder! Let’s dive into the fascinating world of TypeScript and its knack for value-based typing. You might have come across the usual stuff, like using the ternary operator or the extends keyword for conditional typing in TypeScript. But hey, there’s a whole new level to this game, and it’s all about conditional types based on actual values.
Getting Down to Basics
So, remember that snippet you might have stumbled upon:
type ValueType<T> = T extends number ? "Number" : "Not a Number";
Yeah, that’s the tip of the iceberg. Our mission here is to dig deeper into the world of conditional typings, especially when they’re based on the actual values themselves.
Unleashing the Power of Values
Let’s paint a scenario involving an order object. It’s got some optional stuff like estimatedDelivery
, trackingNumber
, deliveryDate
, and cancellationReason
. But here’s the catch: they’re all optional, and that can be a headache when you need them but they’re not there.
type OrderStatus = {
status: string;
estimatedDelivery?: Date;
trackingNumber?: string;
deliveryDate?: Date;
cancellationReason?: string;
};
Enter value-based conditional types! We’re refining the OrderStatus
type to make it super specific to different scenarios:
type OrderStatus = { status: string } & (
{
status: 'pending';
estimatedDelivery: Date;
}
| {
status: 'shipped';
trackingNumber: string;
}
| {
status: 'delivered';
deliveryDate: Date;
}
| {
status: 'cancelled';
cancellationReason: string;
}
);
Making Life Easier with Smart Functions
Now, let’s up our game by writing functions that perfectly match up with different OrderStatus
situations:
function getOrderStatus(order: OrderStatus): string {
if (order.status === 'pending') {
return `Your order is pending. Estimated delivery: ${order.estimatedDelivery}`;
} else if (order.status === 'shipped') {
return `Your order has been shipped. Tracking number: ${order.trackingNumber}`;
} else if (order.status === 'delivered') {
return `Your order has been delivered on ${order.deliveryDate}`;
} else if (order.status === 'cancelled') {
return `Your order has been cancelled. Reason: ${order.cancellationReason}`;
} else {
return 'Invalid order status';
}
}
In the screenshot below we can see the typescript complaining about missing estimatedDelivery when status is pending.
Let’s Get Fancy with Mapping Types
We’re going even smoother now! By creating a mapping type, we’re tying up each OrderStatus
with its own unique message generator function:
type OrderStatusMessages = {
'pending': (order: { status: 'pending'; estimatedDelivery: Date }) => string;
'shipped': (order: { status: 'shipped'; trackingNumber: string }) => string;
'delivered': (order: { status: 'delivered'; deliveryDate: Date }) => string;
'cancelled': (order: { status: 'cancelled'; cancellationReason: string }) => string; // Added handler for cancelled status
};
function getOrderStatusConditional(order: OrderStatus): string {
const messageGenerator = orderStatusMessages[order.status];
if (messageGenerator) {
return messageGenerator(order);
} else {
return 'Invalid order status';
}
}
Wrapping It Up
So, there you have it! TypeScript’s value-based conditional typing is like a superpower for coders. It helps you write code that’s not just efficient but also super adaptable to whatever comes its way.
By understanding, implementing, and mastering these cool tricks, you’re taking your coding game to a whole new level!
Remember, embrace those value-based conditional types in TypeScript and open doors to a whole new world of coding awesomeness.
Let me know your thoughts