Skip to content

[BUG] Issue with union and literal types #18365

Closed

Description

There seems to be a problem with unions and literal types when both types of the union contain a field with the same name.

TypeScript Version: 2.5.2

Code
The small example

class A {
    field: 42;
}

class B {
    field: number;
    otherfield: number;
}

// Gives an error
let x: A | B = {
    field: 42
};
// Type '{ field: number; }' is not assignable to type 'A | B'.
//  Type '{ field: number; }' is not assignable to type 'B'.
//    Property 'otherfield' is missing in type '{ field: number; }'.

The simple example of a more realistic situation (typeorm-ish code)

type OrderBy = 'ASC' | 'DESC';

type SimpleOrderByCondition = {
    name?: OrderBy;
    randomField?: OrderBy;
};

type OrderByCondition = SimpleOrderByCondition | (() => SimpleOrderByCondition);

// Works OK as expected
let orderBy1: OrderByCondition = {
    randomField: 'ASC'
};

// gives an error. But, it should work.
let orderBy2: OrderByCondition = {
    name: 'ASC'
};

Expected behavior:
No error

Actual behavior:

[ts]
Type '{ name: string; }' is not assignable to type 'OrderByCondition'.
Type '{ name: string; }' is not assignable to type '() => SimpleOrderByCondition'.
Type '{ name: string; }' provides no match for the signature '(): SimpleOrderByCondition'.

Activity

  1. olegdunkan commented on Sep 10, 2017

    @olegdunkan

    First example is about fresh literal object type, check this:

    #3823

    UPDATE 1: This PR also tightens certain parts of the implementation of union types. Specifically:

    We no longer perform subtype reduction on union types but instead perform a non-lossy deduplication of the constituent type set.

    class A {
        field: 42;
    }
    
    class B {
        field: number;
        otherfield: number;
    }
    
    var y: A;
    
    y = {
        field: 42
    };
    
    let x: A | B = y; //OK

    or

    class A {
        field: number; //change type to number and it will be working
    }
    
    class B {
        field: number;
        otherfield: number;
    }
    
    let x: A | B = {
        field: 42
    }; //OK, because type of {field:42} is not {field:42} it is {field:number}, 

    Second case is more interesting.
    It is the same as above but name is a intrinsic property of function object, The type with property name looks like an candidate for Function type but not the same because it has only name property.
    Non-lossy deduplication of the constituent type set may expect all properties of Function to be in object literal.

  2. mhegazy commented on Sep 11, 2017

    @mhegazy
    Contributor

    When a literal is contextually typed by it is base type (number, string, or an enum) we should still infer the literal type. I am sure i have seen this reported before but can not seem to find the issues to reference here.

  3. mhegazy commented on Oct 9, 2017

    @mhegazy
    Contributor

    Seems like a duplicate of #16457

  4. locked and limited conversation to collaborators on Jun 14, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

BugA bug in TypeScriptDuplicateAn existing issue was already created

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions