Exploring the Concepts of Union and Intersection Types in Typescript

I am trying to wrap my head around Union and Intersection types in TypeScript, and I've come across a case that's puzzling me. You can check it out on this 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,
}

I'm confused about why I can assign values to both properties in the intersection type. Logically, the intersection between the two interfaces should be empty. If I interpret the & as an AND, then it makes sense why I can include both properties. However, if I treat the | keyword as an OR, I would expect to only be able to assign either a or b, not both.

Could someone provide some insight into these types?

Answer №1

Considering the given:

interface X {
    x: number;
    y: number;
}

interface Y{
    z: boolean;
    y: number;
}

An instance of Union type X | Y can be assigned to either interface X or Y. It must contain properties from either X or Y (or both)

const objX: X | Y = {
    x: 6,
    y: 6
}

const objY: X | Y = {
    z: true,
    y: 6
}

const objZ: X | Y = {
    x: 6,
    z: true,
    y: 6
}

What operations are possible with a type like X | Y? Only those that are common in both X and Y

objX.y = 1; // valid

Intersection type X & Y, if it is assignable to both interfaces X and Y (and therefore must have properties from both X and Y).

const object: X & Y = {
    x: 6,
    z: true,
    y: 1
}

Update

You may question, "why can 'X & Y' in your example have property 'z'? It's not part of type X."

This statement is not accurate. Any type that includes all properties of X can be assigned to X. Additional properties do not pose an issue:

const xAndExtraProp = {
  x: 6,
  y: 1,
  z: 6
};

const cx0: X = xAndExtraProp;

You might be confused by Excess Property Checks for object literals:

Object literals undergo special handling and excess property checking when being assigned to other variables or used as arguments. If an object literal contains any properties that the “target type” does not possess, an error will occur:

const cx1: X = {
  x: 6,
  y: 1,
  z: 6 //ERROR
};

Answer №2

Consider assigning values to these types rather than trying to make the types intersect and merge.

type I = string & number;
type U = string | number;

const i: I = 5;

This attempt fails because we want i to be both a string and a number simultaneously, which is impossible (I is never in this case).

const u: U = 5;

u can either be a string OR a number, so it's acceptable

Now referring back to your example:

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

This works fine because obj2 intersects with IntersectionType (it has both properties a and b). TypeScript checks the properties in obj2 and confirms that all necessary properties of IntersectionType are present.

Additionally,

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

TypeScript analyzes obj... Does obj have the required properties in A? Yes. This satisfies TS.

If you were to add property c to both objects, TypeScript would fail as it is an unknown property for both types, but that's a different issue altogether.

Answer №3

Consider the perspective of what a union type can accept. The combination of values that can be assigned to a string | number field includes all strings as well as all numbers. However, when it comes to a string & number field, there is no intersection between the sets of all strings and all numbers.

It seems like you may be viewing this as an operator on the fields themselves rather than understanding it as operations on the sets represented by each type.

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

In mathematical terms, a "type" represents a set of all its possible values, for example boolean = {true, false};. The union of types T and U combines the values found in T or U (T merged with U), while the intersection involves values present in both T and U (the common part).

A member is considered accessible if it exists within all possible values. With unions, a value of type T|U could be either T or U, leading us to only consider common attributes as permissible. It's important to note that operations performed on types have implications for their members, following de Morgan laws.

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

Answer №4

Here are some illustrations to help you grasp the concept of TS unions and intersections:

interface X {
  x1: number,
  x2: number,
}

interface Y {
  y1: number,
  y2: number;
}

type UnionXY = X | Y;
type IntersectionXY = X & Y;

const unionXY1: UnionXY = {
  x1: 123,
  x2: 456,
  y1: 789,
  y2: 101112,
};

const unionXY2: UnionXY = {
  x1: 123,
  x2: 456,
};

const unionXY3: UnionXY = {
  y1: 789,
  y2: 101112,
};

// Error
// Property 'x1' does not exist on type 'Y'.
console.log(unionXY3.x1);

const unionXY4: UnionXY = {
  x1: 123,
  x2: 456,
  y2: 101112,
};

// Error
// Property 'y1' does not exist on type 'UnionXY'.
// Property 'y1' does not exist on type 'X'.
console.log(unionXY4.y1);


// Error
// Type '{ x1: number; y2: number; }' is not assignable to type 'UnionXY'.
// Property 'y1' is missing in type '{ x1: number; y2: number; }' but required in type 'Y'.
const unionXY5: UnionXY = {
  x1: 123,
  y2: 101112,
};

const intersectionXY1 : IntersectionXY = {
  x1: 123,
  x2: 456,
  y1: 789,
  y2: 101112,
};

// Error
// Type '{ x1: number; x2: number; y1: number; }' is not assignable to type 'IntersectionXY'.
// Property 'y2' is missing in type '{ x1: number; x2: number; y1: number; }' but required in type 'Y'.
const intersectionXY2 : IntersectionXY = {
  x1: 123,
  x2: 456,
  y1: 789,
};

// Error
// Type '{ x2: number; y1: number; y2: number; }' is not assignable to type 'IntersectionXY'.
// Property 'x1' is missing in type '{ x2: number; y1: number; y2: number; }' but required in type 'X'.
const intersectionXY3 : IntersectionXY = {
  x2: 456,
  y1: 789,
  y2: 101112,
};

Answer №5

Let's delve into the meanings behind their names, rather than focusing on the symbols used.

This means we shouldn't see the Union '|' as a strictly exclusive operation, or the Intersection '&' as merely a merging of elements.

When we consider the meaning of Union, it represents a coming together of multiple entities, whereas Intersection signifies where two or more elements intersect.

For instance:

interface Human{
 firstName: string;
 lastName: string;
 }

interface Programmer{
 programmingLanguage: string;
 salary: number;
}

//values can be obtained from both interfaces
const UnionType: Human|Programmer={firstName: "Will",lastName: "Smith",programmingLanguage:"Typescript"};


//no values can be drawn from both interfaces due to lack of common ground, however all values are accessible, as in this scenario, the intersection encompasses all values from both interfaces.
const IntersectionType: Human & Programmer={firstName: "Will",lastName: "Smith",programmingLanguage:"TypScript",salary:2300};

I highly recommend checking out this article TypeScript and Set Theory, for a deeper grasp of the concepts surrounding Union & Intersection.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips on configuring a segment in an Angular 6 route

Question: I am looking to configure a specific segment after the user logs in, for example http://localhost:4200/#/{dynamic name}/{dynamic name}/app/... However, I am facing an issue when navigating to /app/... across the application. Is there a way to a ...

Using NPM in combination with React and TypeScript to incorporate AMD modules

I am currently in the process of setting up a development environment for an application that is written in TypeScript using React. I already have existing TypeScript code that compiles to ES5 and AMD modules. My goal is to avoid any JavaScript transpilat ...

The sequence of initializing test hooks in inconsistent playwright tests

My testing framework setup looks something like this: test.describe("...", () => { let p: Page; test.beforeEach(async({browser}) => { p = await (await browser.newContext()).newPage(); } test(...); test(...); test.aft ...

Retrieve active route information from another component

We are utilizing a component (ka-cockpit-panel) that is not linked to any route and manually inserted into another component like so: .. ... <section class="ka-cockpit-panel cockpit-1 pull-left"> <ka-cockpit-panel></ka- ...

Dealing with implicit `any` when looping through keys of nested objects

Here is a simplified example of the issue I am facing: const testCase = {a:{b:"result"}} for (const i in testCase) { console.log("i", i) for (const j in testCase[i]){ console.log("j", j) } } Encountering ...

Exiting a void method in JavaScript/Typescript using the return or break statement

I find myself dealing with a complex method in Typescript that contains multiple if else if else constructs. It's a void method, and I'm wondering how I can exit the method based on a specific if condition without having to execute the remaining ...

Type errors in NextJS are not being displayed when running `npm run dev`

When encountering a typescript error, I notice that it is visible in my editor, but not in the browser or the terminal running npm run dev. However, the error does show up when I run npm run build. Is there a method to display type errors during npm run d ...

Steps for generating data with Typescript Sequelize model without specifying an id:

Currently, I am utilizing Sequelize in conjunction with Typescript and facing a challenge when attempting to execute the Course.create() method without explicitly specifying an id field. Below is the Course model for reference: import { DataTypes, Model, O ...

Creating Mongoose models in TypeScript with multiple connections

Attempting to build a model with multiple connections as per the documentation, I encountered the following error: TS2345: Argument of type 'Schema<Document<any>, Model<Document<any>>>' is not assignable to parameter of ty ...

Modify the name of the document

I have a piece of code that retrieves a file from the clipboard and I need to change its name if it is named image.png. Below is the code snippet where I attempt to achieve this: @HostListener('paste', ['$event']) onPaste(e: ClipboardE ...

Limit the parameter of a TypeScript method decorator based on the method's type

Is it possible to generically restrict the argument of a method decorator based on the type of the method it is applied to? I attempted to obtain the method's type that the decorator is applied to using TypedPropertyDescriptor<Method>. However, ...

ERROR: The use of @xenova/transformers for importing requires ESM

When working with my Node.js application, I usually start by importing the necessary modules like this: import { AutoModel, AutoTokenizer } from '@xenova/transformers'; Afterwards, I utilize them in my code as shown below: const tokenizer = awai ...

What could be causing the issue where only one of my videos plays when hovered over using UseRef?

I'm currently working on a project where I have a row of thumbnails that are supposed to play a video when hovered over and stop when the mouse moves out of the thumbnail. However, I've encountered an issue where only the last thumbnail plays its ...

Is there a way to carry out tests on keydown events within Jasmine by specifying the keyCode within an Angular2+

I am working on a project where I need to trigger keydown events on an <input> field. Tools Used Karma v1.7.1 as the test runner Tests running on Chrome browser Using Angular 5 framework Any insights on how I can achieve this? ...

Retrieve the value of a variable to access an object property dynamically in React Native using TypeScript

As I attempted to create dynamic styles for this component, my goal was to determine the styles based on a string passed in as a property. Here is the code snippet: export type MyComponentProps = { styleName: string; } const MyComponent = (props: MyComp ...

Exploring Angular's filtering capabilities and the ngModelChange binding

Currently, I am in the process of working on a project for a hotel. Specifically, I have been focusing on developing a reservation screen where users can input information such as the hotel name, region name, check-in and check-out dates, along with the nu ...

Customizing a generic method in typescript

I am currently working on developing a base class called AbstractQuery, which will serve as a parent class to other classes that inherit from it. The goal is to override certain methods within the child classes. class AbstractQuery { public before< ...

Tips for adjusting the angle in SVG shapes

I have designed an svg shape, but I'm struggling to get the slope to start from the middle. Can someone please provide assistance? <svg xmlns="http://www.w3.org/2000/svg" fill="none"> <g filter="url(#filter0_b_1_2556)"&g ...

What is the best way to prevent double clicks when using an external onClick function and an internal Link simultaneously

Encountering an issue with nextjs 13, let me explain the situation: Within a card component, there is an external div containing an internal link to navigate to a single product page. Using onClick on the external div enables it to gain focus (necessary f ...

Oops! There seems to be an issue with the code: "TypeError: this

I am just starting out with Angular. Currently, I need to assign a method to my paginator.getRangeLabel (I want to use either a standard label or a suffixed one depending on certain conditions): this.paginator._intl.getRangeLabel = this.getLabel; The cod ...