YouTip LogoYouTip

Ts Conditional Types

Conditional Types is one of the most powerful features in the TypeScript type system. It allows dynamically selecting types based on conditions, similar to ternary expressions in programming languages. Conditional Types make type definitions more flexible and are the foundation for implementing advanced utility types. * * * * * * ## Why Conditional Types Are Needed In actual TypeScript development, we often need to return different types based on different input types. For example, a function might accept string or number parameters, and we need to return different result types based on the parameter type. Conditional Types provide the ability to perform logical judgments at the type level, making type definitions more flexible and powerful. > **Concept Explanation:** The syntax for Conditional Types is `T extends U ? X : Y`. If type T can be assigned to type U, then return type X, otherwise return type Y. * * * ## Basic Syntax Conditional Types use ternary expression syntax to perform conditional checks at the type level. This allows us to dynamically compute return types based on input types. ## Example // Conditional Type syntax: T extends U ? X : Y // If T is string type, return true, otherwise return false type IsString= T extends string ?true:false; // Using Conditional Types // string extends string is true, so type A is true type A = IsString; // number extends string is false, so type B is false type B = IsString; // Using these types var a: A =true; var b: B =false; console.log("string is string?: "+ a); console.log("number is string?: "+ b); **Result:** string is string?: true number is string?: false > **Explanation:** Conditional Types are automatically evaluated during type checking to generate concrete types. * * * ## Practical Application: Type Filtering One of the most common applications of Conditional Types is filtering types. For example, you can create a type to exclude null and undefined. ## Example // Using Conditional Types to implement NonNullable // If T is null or undefined, return never (empty type), otherwise return T itself type NonNullable= T extends null|undefined? never : T; // Using NonNullable type // string is not null/undefined, so type is string type A = NonNullable; // null is null/undefined, so type is never type B = NonNullable; // undefined is null/undefined, so type is never type C = NonNullable; // Verifying types var a: A ="hello"; console.log("Non-null: "+ a); **Result:** Non-null: hello > **never Type:** never represents a type that never exists. When conditions are not met, TypeScript uses never to represent an "unavailable" type. * * * ## Type Inference: infer Keyword The infer keyword is one of the most powerful features in Conditional Types. It allows "extracting" or "inferring" specific type parts from a type. ## Example // Using infer to infer function return type // If T is a function type, return the inferred return type R, otherwise return never type ReturnType= T extends(...args: any[])=> infer R ? R : never; // Define a function that returns a user object function getUser(){ return{ name:"Alice"}; } // Define a function that returns a number function getNumber(){ return 42; } // Using ReturnType to get function return types // Inferred as { name: string } type R1 = ReturnType; // Inferred as number type R2 = ReturnType; // Using inferred types var r1: R1 ={ name:"Bob"}; var r2: R2 =100; console.log("User: "+ JSON.stringify(r1)); console.log("Number: "+ r2); **Result:** User: {"name":"Bob"}Number: 100 > **Role of infer:** infer is like a "variable" in the type system that can capture specific parts of a type and use them in the result type. * * * ## Distributive Conditional Types When the generic parameter of a Conditional Type is a union type, it automatically performs "distribution" processing. In other words, the condition is executed separately for each member of the union type, and then the results are merged. ## Example // ToArray converts type T to an array type // When T is a union type, it automatically distributes processing for each type type ToArray= T extends any ? T[]: never; // Union types are automatically distributed // string | number is distributed as: ToArray | ToArray // Which is: string[] | number[] type StrOrNum = ToArray; // Can assign string[] or number[] var arr: StrOrNum =; // Can also assign number arr =42; console.log("Array: "+ arr); **Result:** Array: 42 > **Distribution Mechanism:** The distribution feature of Conditional Types is automatically enabled. To disable distribution, you can use square brackets: ` extends U`. * * * ## Combining Conditional Types with Mapped Types Conditional Types can be combined with Mapped Types to create powerful type transformation tools. This combination is the foundation for implementing TypeScript's built-in utility types. ## Example // Define User interface interface User { // User ID id: number; // Username name: string; // User email email: string; } // Using Mapped Types and Conditional Types to implement Partial // Iterate over all properties of T, add optional modifier ? type Partial={ ?: T; }; // Using Mapped Types and Conditional Types to implement Required // Iterate over all properties of T, remove optional modifier -? type Required={ -?: T; }; // Using Partial: all properties become optional var partial: Partial={ name:"Alice"}; // Using Required: make properties of Partial required // Need to have Partial type first type RequiredUser = Required<Partial>; var required: RequiredUser ={ name:"Bob", id:1}; console.log("Optional: "+ JSON.stringify(partial)); console.log("
← Ts Class InheritanceTs Optional Chaining β†’