thecotne
thecotne

Reputation: 478

confused with disjoint unions

i am trying to have union of possible objects and to transform one to another

but both typescript and flow fail to recognize that boolean is true or false and nothing else and it's ok to assign boolean to object onion where it needs ether true literal or false literal

type Aa = {
  isFetching: false,
  isFailed: false,
  isFetched: false
}

type Bb = {
  isFetching: true,
  isFailed: false,
  isFetched: false
}

type Cc = {
  isFetching: true,
  isFailed: true,
  isFetched: false
}
type Dd = {
  isFetching: false,
  isFailed: true,
  isFetched: false
}

type Data = Aa | Bb | Cc | Dd

function thisWorks(data: Data): Data {
  if (data.isFailed) {
    return {
      isFetching: true,
      isFailed: true,
      isFetched: data.isFetched
    }
  } else {
    return {
      isFetching: true,
      isFailed: false,
      isFetched: data.isFetched
    }
  }
}

function thisDoesNot(data: Data): Data {
  return {
    isFetching: true,
    isFailed: data.isFailed,
    isFetched: data.isFetched
  }
}

is this bug or not?

flow.org/try

typescript Playground

Upvotes: 2

Views: 137

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250396

The problem is that the object literal is compatible with neither Bb or Cc, since isFailed is boolean (since the field is common to all members of the union it will become true|false which is boolean)

The simplest solution is to use a type assertion to the union. This will preserve some checks but allow the assignment to occur:

function thisDoesNot(data: Data): Data {
    return {
        isFetching: true,
        isFailed: data.isFailed,
        isFetched: data.isFetched
    } as Data
}

this would be an error:

function thisDoesNot(data: Data): Data {
    return {
        isFetching: 1,
        isFailed: data.isFailed,
        isFetched: data.isFetched
    } as Data
}

We do lose excess property checks though, this would be valid:

function thisDoesNot(data: Data): Data {
    return {
        excess: 1,
        isFetching: true,
        isFailed: data.isFailed,
        isFetched: data.isFetched
    } as Data
}

Upvotes: 1

Related Questions