Emmanuel Touzery
Emmanuel Touzery

Reputation: 9183

typescript conditional types: inference works but the compiler rejects the implementation

I've used typescript's conditional types with great success, but often I find that while I can express the type signatures, in the implementation of the functions I must use any even though I know the implementation is correct.

Example, this definition:

type ExportType<InputType extends string|undefined> =
    string extends InputType ? undefined : ()=>void;

function convert<InputType extends string|undefined>(input: InputType): ExportType<InputType>;

(I know I could use overloads, but my real issue is more complex and overloads wouldn't be realistic for the real issue)

In effect the signature is saying "if the parameter is string, I'll return ()=>void, if it's undefined, I'll return undefined.

And this works great. Playing with convert("x") and convert(undefined) shows that typescript agrees with this definition.

However when actually trying to implement this function:

type ExportType<InputType extends string|undefined> =
    string extends InputType ? undefined : ()=>void;

function convert<InputType extends string|undefined>(input: InputType): ExportType<InputType> {
    if (input) {
        return ()=>{console.log("OK");};
    } else {
        return undefined;
    }
}

This does not compile. Typescript says:

error TS2322: Type '() => void' is not assignable to type 'ExportType<InputType>'.

         return ()=>{console.log("OK");};
         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

But that line is in if (input), therefore at this point we know that the input is not undefined, so it must be string, and therefore we must return () => void... But it seems the compiler is just not smart enough to see that.

Changing that line to:

    return <any>(()=>{console.log("OK");});

Makes it work, but it's disappointing...

So is there a way to write a fully type-safe and checked implementation of this signature that typescript lets us express?

Upvotes: 0

Views: 296

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250396

Typescript will not allow you to assign a value to an unresolved conditional type (ie one that still contains free type parameters). You can use a cast as you discovered or you can use a separate implementation signature:

type ExportType<InputType extends string|undefined> =
    string extends InputType ? undefined : ()=>void;

function convert<InputType extends string|undefined>(input: InputType): ExportType<InputType> 
function convert(input: string|undefined): undefined | (()=>void) {
    if (input) {
        return ()=>{console.log("OK");};
    } else {
        return undefined;
    }
}

Upvotes: 2

Related Questions