Skip to content
webtype.orgwebtype.orgTwelve concepts · 6/12

    ↑↓ move · ⏎ open · esc close

    Concept 6 of 12

    Conditional types

    A conditional type is the type-level `if`. The question it asks is always the same one: is the left side assignable to the right? Not "are these equal", not "is this an instance of" — assignable, one direction only.

    Assignability, not equality

    type A = 'hello' extends string ? true : false   // true
    type B = string extends 'hello' ? true : false   // false
    type C = never extends string ? true : false     // true — never fits anywhere
    type D = { a: 1; b: 2 } extends { a: 1 } ? true : false  // true

    A narrower type is assignable to a wider one, so the test is directional. An object with extra properties is assignable to one with fewer, which is why `extends` cannot be used to check that two object types are the same shape.

    When you genuinely need "the same type", you need the `Equal` helper every check on this site is built on — two deferred conditionals compared for identity, which is stricter than assignability in both directions.

    Branches are tested in order

    type Describe<T> = T extends (...args: never[]) => unknown
      ? 'function'
      : T extends unknown[]
        ? 'array'
        : T extends object
          ? 'object'
          : 'primitive'

    Narrower cases have to come first. Functions and arrays are both objects, so testing `extends object` early would swallow them — the classic cause of a `DeepReadonly` that quietly destroys every function it touches.

    The common wrong answer

    // Intent: is T exactly string?
    type IsString<T> = T extends string ? true : false
    
    IsString<'hello'>  // true — but 'hello' is not string, it is narrower
    IsString<any>      // boolean — any matches both branches at once

    `extends` answers "fits into", so every string literal passes a test for `string`. And `any` is assignable to everything, so a conditional given `any` returns the union of both branches — a result that is almost never what the author intended.

    Exercise

    Answer `true` when `T` is a `string`, `false` otherwise.

    Try it

    2
    • IsString<'a'>
      true
    • IsString<number>
      false

    Takeaway

    Conditionals ask about assignability and test branches in order. Narrow first, and reach for `Equal` the moment you need actual sameness.

    Practiced in