Conditional types can extract the payload type from a tagged response shape.

payload extraction Combining `extends` with `infer` can extract a nested type while a runtime branch handles success and failure.

Response Payload Type

okResponse
payload.ts
Replay: real traced execution (multi-file project)
type Success<T> = { ok: true; value: T };
type Failure = { ok: false; error: string };
type Response<T> = Success<T> | Failure;

type PayloadOf<T> = T extends Success<infer U> ? U : string;

function describeResponse<T>(response: Response<T>): string {
    if (response.ok) {
        const payload: PayloadOf<Success<T>> = response.value;
        return `value:${payload}`;
    }
    const payload: PayloadOf<Failure> = response.error;
    return `error:${payload}`;
}

const okResponse: boolean = true;
const response: Response<number> = okResponse ? { ok: true, value: 7 } : { ok: false, error: "missing" };

console.log(describeResponse(response));
type Success<T> = { ok: true; value: T };
type Failure = { ok: false; error: string };
type Response<T> = Success<T> | Failure;

type PayloadOf<T> = T extends Success<infer U> ? U : string;

function describeResponse<T>(response: Response<T>): string {
    if (response.ok) {
        const payload: PayloadOf<Success<T>> = response.value;
        return `value:${payload}`;
    }
    const payload: PayloadOf<Failure> = response.error;
    return `error:${payload}`;
}

const okResponse: boolean = false;
const response: Response<number> = okResponse ? { ok: true, value: 7 } : { ok: false, error: "missing" };

console.log(describeResponse(response));
  1. okResponse ← true, response ← [object Object]

    13    return `error:${payload}`;14}1516const okResponse→ true: boolean = true;  //@okResponse=false17const response→ [object Object]: Response<number> = okResponsetrue ? { ok: true, value: 7 } : { ok: false, error: "missing" };1819console.log(describeResponse(response[object Object]));
  2. function describeResponse<T>(response: Response<T>): string

    5type PayloadOf<T> = T extends Success<infer U> ? U : string;67function describeResponse<T>(response[object Object]: Response<T>): string {8    if (response.ok) {
  3. payload ← 7, response.value ← 7

    7function describeResponse<T>(response: Response<T>): string {8    if (response.oktrue) {9        const payload→ 7: PayloadOf<Success<T>> = response.value→ 7;10        return `value:${payload7}`;11    }
    values this step[object Object]response
  4. console.log(describeResponse(response));

    16const okResponse: boolean = true;  //@okResponse=false17const response: Response<number> = okResponse ? { ok: true, value: 7 } : { ok: false, error: "missing" };1819console.log(describeResponse(response[object Object]));
    outputvalue:7
  1. okResponse ← false, response ← [object Object]

    13    return `error:${payload}`;14}1516const okResponse→ false: boolean = false;17const response→ [object Object]: Response<number> = okResponsefalse ? { ok: true, value: 7 } : { ok: false, error: "missing" };1819console.log(describeResponse(response[object Object]));
  2. payload ← missing, response.error ← missing

    5type PayloadOf<T> = T extends Success<infer U> ? U : string;67function describeResponse<T>(response[object Object]: Response<T>): string {8    if (response.ok) {9        const payload: PayloadOf<Success<T>> = response.value;10        return `value:${payload}`;11    }12    const payload→ missing: PayloadOf<Failure> = response.error→ missing;13    return `error:${payloadmissing}`;14}
  3. console.log(describeResponse(response));

    16const okResponse: boolean = false;17const response: Response<number> = okResponse ? { ok: true, value: 7 } : { ok: false, error: "missing" };1819console.log(describeResponse(response[object Object]));
    outputerror:missing