Skip to content

Generic types combined with & instead of |Β #48287

Closed

Description

@RoelVB

πŸ”Ž 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 |

Activity

  1. whzx5byb commented on Mar 16, 2022

    @whzx5byb

    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.

  2. RoelVB commented on Mar 16, 2022

    @RoelVB
    Author

    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

  3. fatcerberus commented on Mar 16, 2022

    @fatcerberus

    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.

  4. RoelVB commented on Mar 16, 2022

    @RoelVB
    Author

    Thanks Bruce Pascoe (@fatcerberus), I understand now

  5. locked as resolved and limited conversation to collaborators on Oct 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions