Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Provide one of object types in typescript

If I type the following:

interface A {
    x: number
}
interface B {
    y: number
}

type Z = A | B;

// here it allows me to create a variable of type Z with both members of type A and B.
let z: Z = {
    x: 5,
    y: 6,
}

I'm not able to ensure that object of type Z does contain all members of A but does not contain members of B (or the opposite). Is it possible at all with TypeScript? After much research I'm leaning to the "no" answer but I'm not sure.

like image 275
lort Avatar asked Nov 15 '17 19:11

lort


1 Answers

By default unions merge all options offered into one, but there are two options for you. What you want exactly is not available in TypeScript, but there are similar issues in the TS project's list (status: "open" at this time) as well as two good workarounds.

One option is not available: At this time there are no exact types in TypeScript (unlike in Flow). There is TS issue #12936 "Exact Types" that right now still is open.

There is another issue/proposal in TS's list of open issues that asks exactly for what your question is about: #14094 "Proposal: Allow exclusive unions using logical or (^) operator between types"

You can use these workarounds:

Option #1

In TypeScript as well as in Flow you can use tagged types to created an XOR union instead of an OR one.

interface A {
    kind: 'A',
    x: number
}
interface B {
    kind: 'B',
    y: number
}

type Z = A | B;

// here it DOES NOT allow to create a variable of type Z with both members of type A and B.
let z: Z = {
    kind: 'A',
    x: 5
    // y: 6 will produce an error
}
  • TS documentation on Discriminated Unions

  • Blog post about tagged union types introduced in TS 2.0 (link).

Option #2

The second option is to set all properties on all types, but set those that are not supposed to exist to undefined:

interface A {
    x: number,
    y?: undefined
}
interface B {
    x?: undefined,
    y: number
}

type Z = A | B;

let z: Z = {
    y: 5
    // y: 6 will produce an error
}

For the record, in Facebook's type system Flow you can solve the problem by using a disjoint union (XOR) instead of just a union (OR) or by using exact object types or as for TS above setting the undesirable property to undefined. I made a Flow demo with your example (link). In this case the code is the same for Flow as for TS.

like image 191
Mörre Avatar answered Oct 06 '22 10:10

Mörre