Skip to content

Generic types combined with & instead of | #48287

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
RoelVB opened this issue Mar 16, 2022 · 4 comments
Closed

Generic types combined with & instead of | #48287

RoelVB opened this issue Mar 16, 2022 · 4 comments

Comments

@RoelVB
Copy link

RoelVB commented Mar 16, 2022

🔎 Search Terms

generic, type, class, and, &, or, |, combined

⏯ Playground Link

Playground

💻 Code

class ExampleClass<T>
{
    test(value: T): T
    {
        return value;
    }
}

class Example1 extends ExampleClass<String> { }
class Example2 extends ExampleClass<Number> { }

function Example(example: 1|2) // returns `typeof Example1 | typeof Example2`
{
    return example===1?Example1:Example2;
}

const instance = new (Example(1))();
instance.test(''); // ERROR: Argument of type 'string' is not assignable to parameter of type 'Number & String'

🙁 Actual behavior

Combining classes with generic type results in generic types on arguments to be combined with &.
But get combined with | for return types.

🙂 Expected behavior

Expected the types to be combined with |

@whzx5byb
Copy link

This is a correct error. Here is a simple example in which your expected behavior is unsound:

class ExampleClass<T> {
    test(value: T): T {
        return value;
    }
}

class Example1 extends ExampleClass<string> { 
    test(value: string): string {
        return value.toLowerCase();
    }
}

class Example2 extends ExampleClass<number> { 
    test(value: number): number {
        return Number(value.toFixed(2));
    }
}

function Example(example: 1|2) // returns `typeof Example1 | typeof Example2`
{
    return Math.random() > 0.5 ? Example1 : Example2;
}

const instance = new (Example(1))();

console.log(instance.test('Foo'))
// You expect it return 'foo', but there's a 50% chance of runtime error

See: #30769 for more details.

@RoelVB
Copy link
Author

RoelVB commented Mar 16, 2022

I don't see how this would be correct.
In test(value: T): T the first T becomes String & Number, the second T becomes String | Number

@fatcerberus
Copy link

fatcerberus commented Mar 16, 2022

Yes, because the first T is in an input position (contravariant) and the other is in an output position (covariant). If you have a function and the only thing you know about it is that it accepts either an X or a Y, and will throw if you give it the wrong thing, the only thing you can safely pass to it is X & Y, i.e. a value that satisfies both types. That’s what the error you’re seeing reflects.

@RoelVB
Copy link
Author

RoelVB commented Mar 16, 2022

Thanks @fatcerberus, I understand now

@RoelVB RoelVB closed this as completed Mar 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants