5

I would like to know, how to specify that generic type if same as the one the resolved type of previous argument, when the type can be of multiple types.

TypeScript playground

function add<T extends (number | string)>(a: T, b: T): T {
    if (typeof a === 'string') {
        return a + b;
    } else if (typeof a === 'number') {
        return a + b;
    }
}

add('hello', ' world');
add(1, 1);

I want to be able to tell the compiler that all T are of same type, either number or string. I might missed some syntax. It might be possible with conditional types (to some extend)...

1 Answer 1

1

You can't narrow the type of the generic parameter within the function. So when you test a this will not tell the compiler what the type of b is. And more importantly it will not tell the compiler what the return type of the function needs to be

function add<T extends (number | string)>(a: T, b: T): T {
    if (typeof a === 'string' && typeof b === 'string') {
        let result = a + b; // result is string, we can apply + 
        return result as T; // still an error without the assertion, string is not T 
    } else if (typeof a === 'number' && typeof b === 'number') {
        let result = a + b; // result is number, we can apply +
        return result as T; // still an error without the assertion, number is not T  
    }
    throw "Unsupported parameter type combination"; // default case should not be reached
}

In this case though maybe having a dedicated implementation signature that works on the union instead (meaning no assertion is required) and public signature being the one you previously used.:

function add<T extends number | string>(a: T, b: T): T
function add(a: number | string, b: number | string): number | string {
    if (typeof a === 'string' && typeof b === 'string') {
        return a + b;
    } else if (typeof a === 'number' && typeof b === 'number') {
        return a + b;
    }
    throw "Unsupported parameter type combination"; // default case should not be reached
}
Sign up to request clarification or add additional context in comments.

5 Comments

Doesn't this warn you that the return value may be undefined?
@jcalz without strict null checks no ... I played with in the playground, will add a throw for the default return
This makes me sad... Do they intend to implement it someday?
Not sure, I don't follow the discussions that closely maybe @jcalz knows. Narrowing the generic type seems to be a useful feature (I have seen a LOT of questions around here that asume this would happen) but I am not sure what the implications of that would be
Do they intend to implement what? If T is "hello", then add("hello","hello") would be required to return a value of type "hello", which it won't. So we don't really want the output to be T, but something like T extends number ? number : string, which is certainly implementable (although currently control flow analysis and conditional types don't play well together so you still need either assertions or one permissive overload implementation)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.