Typescript: Same generic type as resolved type of

2020-03-23 17:27发布

问题:

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:

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
}