Home > front end >  Is it possible to make a TypeScript function return never (or throw) if a generic type extends a sub
Is it possible to make a TypeScript function return never (or throw) if a generic type extends a sub

Time:10-21

(There is no genuine use-case here, so this is my code)

I'm trying to make a function that throws an error if the string is "fish". I was successfully able to do this by using as, however, is there any way to avoid using as for this scenario?

function throwIfFish<S extends string>(string: S): S extends "fish" ? never : S {
    if (string === "fish") {
        throw new Error("fish");
    }
    return string as S extends "fish" ? never : S;
    //            ^ is this unavoidable?
}

Typescript Playground

CodePudding user response:

The conditional type in question is simple enough, and the result type is "good enough" so that index signature type can work here:


type NeverFish<S extends string> = {
    fish: never;
    [n: string]: S;
}

function throwIfFish<S extends string>(str: S): NeverFish<S>[S] {
    if (str === "fish") {
        throw new Error("fish");
    } else { 
        return str; 
    }
}


let r1 = throwIfFish("f");    // "f"
let r2 = throwIfFish("fish"); // never


CodePudding user response:

Move the signature into another overload:

function throwIfFish<S extends string>(string: S): S extends "fish" ? never : S;
function throwIfFish(string: string) {
    if (string === "fish") {
        throw new Error("fish");
    }

    return string;
}

but this means TypeScript will no longer check if the implementation coheres to the overload's signature. Alternatively, you could still use a cast with less repetition:

return string as ReturnType<typeof throwIfFish<S>>;
  • Related