Advanced TypeScript: Conditional Types
If you're using TypeScript just for basic interfaces, you're missing out on its true power as a logic engine. 2019 is the year of "type-level programming," and conditional types are the key.
The Syntax
A conditional type looks like a ternary operator in JavaScript:
T extends U ? X : Y
Distributive Conditional Types
When the checked type is a naked type parameter, the conditional type becomes "distributive" over unions.
type ToArray<T> = T extends any ? T[] : never;
type StrOrNumArray = ToArray<string | number>;
// Result: string[] | number[]
The infer Keyword
This is where things get really interesting. infer allows us to extract types from within other types. Let's recreate the ReturnType<T> utility:
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
function getUser() {
return { id: 1, name: "Alice" };
}
type User = MyReturnType<typeof getUser>;
// Result: { id: number; name: string; }
Recursive Type Aliases
With conditional types, we can now handle deeply nested structures. Want to make a DeepPartial<T>?
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends (infer U)[]
? DeepPartial<U>[]
: T[P] extends object
? DeepPartial<T[P]>
: T[P];
};
interface HeavyConfig {
api: {
endpoint: string;
timeout: number;
};
flags: string[];
}
const partial: DeepPartial<HeavyConfig> = {
api: { endpoint: "/v1" }
};
This level of type safety allows us to catch bugs at compile time that were previously impossible to track in vanilla JS. We're not just writing code; we're proving its correctness through the type system.
Aunimeda builds modern web frontends - from single-page applications to complex multi-locale sites.
Contact us to discuss your frontend project. See also: Web Development, Corporate Website Development