4
votes

I am trying to get an intuition about Union and Intersection types in typescript, but I can't figure out this case: Playground Link

interface A {
    a: number;
}

interface B{
    b: boolean;
}



type UnionCombinedType = A | B;
type IntersectionType = A & B;

const obj: UnionCombinedType = {
    a: 6,
    b: true,
}

const obj2: IntersectionType = {
    a: 6,
    b: true,
}

Why am I allow to put both values in the intersection type? The intersection between the two interfaces is empty. If I read the & as AND then it's clear to me why it allows me to add both props, but then I should read the | keyword as OR and I would expect it to allow me to assign only a or b but not both.

Can someone give me some intuition about those types?

2

2 Answers

3
votes

Think from the perspective of what a union type accepts. The set of things that can be assigned to a string | number field is the union of the set of all strings and the set of all numbers. The set of things that can be assigned to a string & number is nothing because there is no overlap in the set of all strings and the set of all numbers.

I think you're picturing it as a set operator on the fields rather than a set operator of the sets of what each type represents.

////////////////////////

This is mathematical terminology: a "type" is a set of all its possible values, e.g. boolean = {true, false};. Now union of types T and U is a set consisting of values in T or in U (i.e. T merged with U); similarly intersection of values in both T and U (i.e. common part of them).

A member is accessible iff it's on all possible values. For union, value of type T|U may be T or may be U. We don't know which so only common attributes are legal. As you can see, operation on type implies different but complementary operation on members. This is a consequence of de Morgan laws.

https://www.reddit.com/r/typescript/comments/9qduq3/why_is_type_intersection_called_like_that_its/

2
votes

Given the following:

interface A {
    a: number;
    c: number;
}

interface B{
    b: boolean;
    c: number;
}

Expression of Union type A | B is assignable to either A or B. It must have properties from A or B (or both)

const oA: A | B = {
    a: 6,
    c: 6
}

const oB: A | B = {
    b: true,
    c: 6
}

const oC: A | B = {
    a: 6,
    b: true
    c: 6
}

But what operations does a type like A | B have? Only these that belong to both A and B

oA.c = 1; // valid

Intersection type A & B, if it is assignable to both A and B (and therefore must have properties of both A and B).

const obj: A & B = {
    a: 6,
    b: true
}

Update

You ask "why does A & B in your example can take b prop? it's not assignable to type A"

This is clearly not true. Any type that has all properties of A can be assigned to A. Extra properties make no harm:

const aAndExtraProp = {
  a: 6,
  d: 6
};

const ca0: A = aAndExtraProp;

You are probably confused by Excess Property Checks for object literals:

Object literals get special treatment and undergo excess property checking when assigning them to other variables, or passing them as arguments. If an object literal has any properties that the “target type” doesn’t have, you’ll get an error:

const ca1: A = {
  a: 6,
  d: 6 //ERROR
};