Krischna Gabriel
Krischna Gabriel

Reputation: 146

"OR" expression with objects does not behave as expected

I have two object types and the input of a function can be either one:

export default function Eingabefeld({
    isText=true,
    children="",
}:(
    {
        isText: true,
        children:string
    } | {
        isText: false,
        children:number
    }
)) { 
    if (isText === true) {
        let data:string = children
        data;
    }else if (isText === false) {
        let data:number = children
        data;
    }
}

Why does TS gives me this error
enter image description here
as if i would have used the "AND" expression to merge the two object types?

Edit: It is a TypeScript bug. See: Discriminated union parameter destructuring doesn't work if the fields have defaults

Upvotes: 1

Views: 63

Answers (1)

Krischna Gabriel
Krischna Gabriel

Reputation: 146

There are multiple ways of solving this.

Accessing the properties can be done in two ways:

  1. Just don't destructure the object
export function Inputfield(props:(
    {
        isText: true,
        children:string
    } | {
        isText: false,
        children:number
    }
)) { 
    if (props.isText === true) {
        let data:string = props.children
    }else if (props.isText === false) {
        let data:number = props.children
    }
}
  1. destructure it after the if statement
export function Inputfield(props:(
    {
        isText: true,
        children:string
    } | {
        isText: false,
        children:number
    }
)) { 
    if (props.isText === true) {
        let {children} = props
        let data:string = children
    }else if (props.isText === false) {
        let {children} = props
        let data:number = children
    }
}

Applying the default values can also be done in two ways, but it will result in different results:

  1. Default values for individual properties
type props_type = {
    isText: true,
    children:string
} | {
    isText: false,
    children:number
}
export function Inputfield(_props:(
    Partial<props_type>
)) { 
    const props = {
        isText: true,
        children: "",
        ..._props
    } as props_type

    if (props.isText === true) {
        let data:string = props.children
    }else if (props.isText === false) {
        let data:number = props.children
    }
}

this can also be written with more bloating if undefined assignments

type props_type = {
    isText: true,
    children:string
} | {
    isText: false,
    children:number
}
function Inputfield(_props:(
    Partial<props_type>
)) { 
    if (_props.isText == undefined) _props.isText = true;
    if (_props.children == undefined) _props.children = "";
    // do NOT use `_props.isText ||= true;`, because if isText is false the default value will be applied

    const props = _props as props_type;

    if (props.isText === true) {
        let data:string = props.children
    }else if (props.isText === false) {
        let data:number = props.children
    }
}
  1. A default object
export function Inputfield(props:(
    {
        isText: true,
        children:string
    } | {
        isText: false,
        children:number
    }
)={
    isText:true,
    children:"",
}) { 
    if (props.isText === true) {
        let data:string = props.children
        data;
    }else if (props.isText === false) {
        let data:number = props.children
        data;
    }
}

When defaults are used as individual values, not assigning a property will use the default value. When giving a default object, only if no input object is given, the default is used.

The ways of accessing the properties and applying the default values may be mixed together as needed.

Upvotes: 1

Related Questions