javascriptflowtype

Cannot assign object literal when creating a union object in JavaScript


I'm trying to write a function that creates an object with all the common attributes of a union type but as far as I can tell it is not possible without a FlowFixMe. Here's an example

type AType = "a";
type BType = "b";
type CType = "c";

type AObj = {
    type: AType,
    common1: boolean,
    common2: boolean,
}

type BObj = {
    type: BType,
    common1: boolean,
    common2: boolean,
}

type CObj = {
    type: CType,
    common1: boolean,
    common2: boolean,
    unique?: string
}

type ObjType = AType | BType | CType;
type XObj = AObj | BObj | CObj;

function createObj(type: ObjType): XObj {
    const myObj: XObj = {
        type: type,
        common1: true,
        common2: false,
    };
    return myObj;
}

const myType: ObjType = "b";
const obj = createObj(myType);

The error I get is this

28:     const myObj: XObj = {                            ^ Cannot assign object literal to `myObj` because: [incompatible-type] Either string literal `b` [1] is incompatible with string literal `a` [2] in property `type`. Or string literal `a` [1] is incompatible with string literal `b` [3] in property `type`. Or string literal `a` [1] is incompatible with string literal `c` [4] in property `type`.
References:
27: function createObj(type: ObjType): XObj {
                             ^ [1]
6:     type: AType,
             ^ [2]
12:     type: BType,
              ^ [3]
18:     type: CType,
              ^ [4]

You can test this on Try Flow


Solution

  • Flow is stricter than TypeScript.

    You will need to use type guards to construct an object of each type:

    function createObj(type: ObjType): XObj {
        const defaultObj = {
            common1: true,
            common2: false,
        }
    
        if (type === "a") return { type, ...defaultObj };
        if (type === "b") return { type, ...defaultObj };
        if (type === "c") return { type, ...defaultObj, unique: "foobar" };
        
        throw new TypeError(`Unknown type: ${type}`);
    }
    

    It seems verbose (and repetitive), but it's required.