Menu
×
   ❮   
HTML CSS JAVASCRIPT SQL PYTHON JAVA PHP HOW TO W3.CSS C C++ C# BOOTSTRAP REACT MYSQL JQUERY EXCEL XML DJANGO NUMPY PANDAS NODEJS DSA TYPESCRIPT ANGULAR GIT POSTGRESQL MONGODB ASP AI R GO KOTLIN SASS VUE GEN AI SCIPY CYBERSECURITY DATA SCIENCE INTRO TO PROGRAMMING BASH RUST

TypeScript Conditional Types


Understanding Conditional Types in TypeScript

Conditional types in TypeScript enable you to create types that depend on other types, similar to how if-else statements work in JavaScript.

They're a powerful feature that allows for sophisticated type transformations and type-level programming.


Key Concepts

  • Type-level logic: Perform conditional checks on types
  • Type inference: Extract and manipulate types using infer
  • Composition: Combine with other TypeScript features
  • Utility types: Build powerful type utilities

Common Use Cases

  • Type-safe function overloading
  • API response type transformations
  • Complex type validations
  • Building reusable type utilities
  • Advanced type inference

Basic Conditional Type Syntax

Conditional types use the form T extends U ? X : Y, which means:

"if type T extends (or is assignable to) type U, use type X, otherwise use type Y".

type IsString<T> = T extends string ? true : false;

// Usage examples
type Result1 = IsString<string>; // true
type Result2 = IsString<number>; // false
type Result3 = IsString<"hello">; // true (literal types extend their base types)

// We can use this with variables too
let a: IsString<string>; // a has type 'true'
let b: IsString<number>; // b has type 'false'
Try it Yourself »


Conditional Types with Unions

Distributive Conditional Types

Conditional types are particularly useful with union types, where they're automatically distributed over union members:

type ToArray<T> = T extends any ? T[] : never;

// When used with a union type, it applies to each member of the union
type StringOrNumberArray = ToArray<string | number>;
// This becomes ToArray<string> | ToArray<number>
// Which becomes string[] | number[]

// We can also extract specific types from a union
type ExtractString<T> = T extends string ? T : never;
type StringsOnly = ExtractString<string | number | boolean | "hello">;
// Result: string | "hello"
Try it Yourself »

Type Inference with infer

Extracting Types from Complex Structures

The infer keyword allows you to declare a type variable within the condition part of a conditional type and then use it in the true branch of the condition:

// Extract the return type of a function type
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// Examples
function greet() { return "Hello, world!"; }
function getNumber() { return 42; }

type GreetReturnType = ReturnType<typeof greet>; // string
type NumberReturnType = ReturnType<typeof getNumber>; // number

// Extract element type from array
type ElementType<T> = T extends (infer U)[] ? U : never;
type NumberArrayElement = ElementType<number[]>; // number
type StringArrayElement = ElementType<string[]>; // string
Try it Yourself »

Built-in Conditional Types

Standard Library Utilities

TypeScript includes several built-in conditional types in its standard library:

// Extract<T, U> - Extracts types from T that are assignable to U
type OnlyStrings = Extract<string | number | boolean, string>; // string

// Exclude<T, U> - Excludes types from T that are assignable to U
type NoStrings = Exclude<string | number | boolean, string>; // number | boolean

// NonNullable<T> - Removes null and undefined from T
type NotNull = NonNullable<string | null | undefined>; // string

// Parameters<T> - Extracts parameter types from a function type
type Params = Parameters<(a: string, b: number) => void>; // [string, number]

// ReturnType<T> - Extracts the return type from a function type
type Return = ReturnType<() => string>; // string
Try it Yourself »

Advanced Patterns and Techniques

Recursive Conditional Types

Conditional types can be used recursively to create complex type transformations:

// Deeply unwrap Promise types
type UnwrapPromise<T> = T extends Promise<infer U> ? UnwrapPromise<U> : T;

// Examples
type A = UnwrapPromise<Promise<string>>; // string
type B = UnwrapPromise<Promise<Promise<number>>>; // number
type C = UnwrapPromise<boolean>; // boolean
Try it Yourself »

Type-Level If-Else Chains

Chain multiple conditions together for complex type logic:

type TypeName<T> =
  T extends string ? "string" :
  T extends number ? "number" :
  T extends boolean ? "boolean" :
  T extends undefined ? "undefined" :
  T extends Function ? "function" :
  "object";

// Usage
type T0 = TypeName<string>; // "string"
type T1 = TypeName<42>; // "number"
type T2 = TypeName<true>; // "boolean"
type T3 = TypeName<() => void>; // "function"
type T4 = TypeName<Date[]>; // "object"
Try it Yourself »

Conditional types are powerful when creating generic utilities and type-safe libraries:

// A function that returns different types based on input type
function processValue<T>(value: T): T extends string
  ? string
  : T extends number
  ? number
  : T extends boolean
  ? boolean
  : never {

  if (typeof value === "string") {
    return value.toUpperCase() as any; // Type assertion needed due to limitations
  } else if (typeof value === "number") {
    return (value * 2) as any;
  } else if (typeof value === "boolean") {
    return (!value) as any;
  } else {
    throw new Error("Unsupported type");
  }
}

// Usage
const stringResult = processValue("hello"); // Returns "HELLO" (type is string)
const numberResult = processValue(10); // Returns 20 (type is number)
const boolResult = processValue(true); // Returns false (type is boolean)
Try it Yourself »

Best Practices

Do:

  • Use conditional types for complex type transformations
  • Combine with infer for type extraction
  • Create reusable type utilities
  • Document complex conditional types
  • Test edge cases in your type definitions

Don't:

  • Overuse complex conditional types when simple types would suffice
  • Create deeply nested conditional types that are hard to understand
  • Forget about performance implications with very complex types
  • Use conditional types for runtime logic

Performance Considerations

  • Deeply nested conditional types can increase compile times
  • Consider using type aliases for intermediate results
  • Be mindful of TypeScript's recursion depth limits



×

Contact Sales

If you want to use W3Schools services as an educational institution, team or enterprise, send us an e-mail:
[email protected]

Report Error

If you want to report an error, or if you want to make a suggestion, send us an e-mail:
[email protected]

W3Schools is optimized for learning and training. Examples might be simplified to improve reading and learning. Tutorials, references, and examples are constantly reviewed to avoid errors, but we cannot warrant full correctness of all content. While using W3Schools, you agree to have read and accepted our terms of use, cookie and privacy policy.

Copyright 1999-2025 by Refsnes Data. All Rights Reserved. W3Schools is Powered by W3.CSS.