TypeScript: Can I use `never` to eliminate branches of a tagged union?

Viewed 48

I have a generic utility type called Result for operations that might return a value or might return an error:

export type Result<T, E> =
    | {ok: true, value: T}
    | {ok: false, err: E};

// Normal use
const r: Result<number, string> = performOperation1();
if (!r.ok) {
    reportError(r.err);
    return;
}
doSomething(r.values);

If I pass in never for the E type parameter, I know that the ok must be true, but TypeScript doesn't:

const r: Result<number, number> = performOperation2();
doSomething(r.value);

Is there a way to get TypeScript to understand that passing in never here eliminates the error branch?

Note: In this tiny example, performOperation2 could just return number instead of Result. But in my full codebase, all of these "operation" functions conform to an interface, which returns Result.

1 Answers

If you can change Result you can add a conditional type to not add the false branch if never is passed in:


export type Result<T, E> =
    | {ok: true, value: T}
    | ([E] extends [never] ? never : {ok: false, err: E} )

declare const r2: Result<number, never>
doSomething(r2.value);

Playground Link

You can also use Exclude to exclude the false branch from union:

declare const r2: Exclude<Result<number, never>, { ok: false}>
doSomething(r2.value);

Playground Link

This is not a direct response to your question, but might be an option if you can't change Result

Related