What is the rationale behind TypeScript occasionally labeling an impossible intersection as 'never'?

There are instances where TypeScript will determine that two types, when intersected, do not have any compatible values. This situation of an empty intersection is known as never, indicating that it is impossible to provide a value that satisfies both types:

type Bread = {
  shape: "loafy"
};
type Car = {
  shape: "carish"
};

// Contradiction1: Resolves to 'never'
type Contradiction1 = Bread & Car;

Interestingly, this behavior appears inconsistent at times. If the conflicting properties are nested within another type, TypeScript may overlook them, causing unexpected outcomes:

// Encapsulate the conflicting types in new types
type Garage = { contents: Car };
type Breadbox = { contents: Bread };

// Contradiction2: Intersection of Garage and Breadbox
// Expected Outcome: Should lead to 'never'
type Contradiction2 = Garage & Breadbox;

Is this a bug in TypeScript? The behavior raises questions about why TypeScript handles these scenarios in such a manner.

Answer №1

This behavior is intentional as property paths in TypeScript have the ability to go incredibly deep while also changing types along the way. For instance, writing code like this is completely valid:

declare class Boxed<T> {
  contents: T;
  doubleBoxed: Boxed<this>
};
declare const b: Boxed<string>
// m: Boxed<Boxed<Boxed<Boxed<Boxed<string>>>>>
const m = b.doubleBoxed.doubleBoxed.doubleBoxed.doubleBoxed;

For any given type, there exists an infinite number of potential properties that "can" exist, each with the possibility of having a unique type never encountered before in your program.

This becomes significant when dealing with the never type, as demonstrated by the following example:

// I am here to cause trouble.
type M<T, S> = T extends { nested: { nested: { nested: any } } } ?
  S :
  { el: T, nested: M<{ nested: T }, S> };

type F1 = {
  prop: M<F1, "foo">
};
type F2 = {
  prop: M<F2, "bar">
};

declare const f1: F1;
// f1.prop.nested.nested.nested: "foo"
f1.prop.nested.nested.nested;

declare const f12: F1 & F2;

// OK, infinitely
f12.prop.el.prop.el.prop.el.prop.el.prop;
// 'never' at depth 4...
f12.prop.nested.nested.nested;

Predicting exactly where you may encounter expressions resulting in never is practically impossible - there are no clues provided in the definition of M; one must thoroughly understand the code as a human to pinpoint where nested nevers may be lurking.

If TypeScript attempted to resolve this for any arbitrary depth of property access, it could potentially make groundbreaking discoveries or solve complex mathematical conjectures such as the Collatz conjecture through type structures that emulate arithmetic operations (which is technically feasible). However, due to the inherent limitations, TypeScript only delves into resolving top-level properties of the produced type.

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

When working with Material-UI and TypeScript, the type '{ children: never[]; }' does not share any properties with the type 'IntrinsicAttributes'. This can lead to incompatible types error messages in your code

As a beginner in TypeScript, I am attempting to incorporate an existing component, specifically tabs from material-ui. Here is the code in SimpleTab.ts: import React from 'react'; import { makeStyles, Theme } from '@material-ui/core/styles ...

Utilizing Typescript generics in scenarios with arguments that may be either a value or a callback function

Here's the issue: I need to pass T, which could be a string, number, boolean, object, array, or a function. The problem is I can't figure out how to handle ab("hello") in this scenario and return T as a value. function a<T>(ab: T | ((v: T) ...

Tips for achieving asynchronous data retrieval using Angular Observable inside another Observable

What is my goal? I have several components with similar checks and data manipulation activities. I aim to centralize these operations in an observable. To do this, I created an observable called "getData" within my service... The unique aspect of "getData ...

What is the best approach for arranging numbers in descending order?

I'm struggling to sort numbers from largest to smallest and need some help. My code successfully sorted numbers with 5 digits, but not the others. Here is a snippet of the unsorted numbers: 15366 13070 13069 13068 13067 13 ...

TypeScript operates under the assumption that every key will be present on a Record object

Check out this code snippet: declare const foo: Record<string, number> const x = foo['some-key'] TypeScript indicates that x is of type number. It would be more accurate to say x is of type number | undefined, as there is no guarantee th ...

Bringing @angular/code into a directory that is not within an Angular project

Currently, I have an Angular 2 project folder with a separate service folder named "auth.service.ts" located outside of it. Within this service file, I am importing `Injectable` from `@angular/core`. However, due to the service being located outside of t ...

Is it possible to execute TypeScript class methods in asynchronous mode without causing the main thread to be blocked?

Creating an app that retrieves attachments from specific messages in my Outlook mail and stores the data in MongoDB. The challenge lies in the time-consuming process of receiving these attachments. To address this, I aim to execute the task in a separate t ...

Is it possible to enable tooltips to function through the innerHTML method?

Currently, I am utilizing the innerHTML attribute to modify the inner HTML of a tag. In this instance, it involves the <td></td> tag, but it could be applied to any tag: <td *matCellDef="let order" mat-cell [innerHTML]="order. ...

Tips for adjusting collisions between models using three.js and oimo.js

I am currently working on implementing collision with an object using three.js by importing a model and then creating a body in Oimo to represent it. My problem arises from the fact that the center of the model does not align with the center of the object ...

Using TypeScript variables within an SCSS file in Vue3 seems to be a challenging task

I'm currently working on a component that has the following template structure: <template> ... <ul class="circle-container"> <li v-for="img in images" :key="img"> <img :src="i ...

Looping through each combination of elements in a Map

I have a Map containing Shape objects with unique IDs assigned as keys. My goal is to loop through every pair of Shapes in the Map, ensuring that each pair is only processed once. While I am aware of options like forEach or for..of for looping, I'm s ...

The search for 'Renderer2' in '@angular/core' did not yield any results

After successfully installing Angular Material in my Angular Project by following the instructions provided in the Material documentation, I encountered some issues. Specifically, when attempting to launch the application with 'npm start', I star ...

Troubleshooting notifications generated by react-hook-form and zod

My customer registration form is causing all my error messages to show as "required." I suspect it might be a misconfiguration in zod or react-hook-form. Below, you'll find the code snippets. This is my generic input component: import { DetailedHTMLP ...

Issue with Next.js: Router.push not causing page to refresh

I'm currently developing a next.js page called /page/data/[dataId] (this page is accessed when a user clicks on a link from the page /page/data, where I fetch the list of items through an API). When a user clicks on a button, I make an API call to de ...

Encountering a Mongoose error during the development of a NestJs project

While working on my Nest project, I encountered an issue with the Mongoose package. When attempting to build the project using npm run build, an error appeared in the console: node_modules/mongoose/node_modules/mongodb/mongodb.d.ts:34:15 - error TS2305: Mo ...

What purpose does the excludScrollbar property serve in react-datepicker?

I'm working on creating a wrapper for a component and I'm using DatePickerProps from the react-datepicker import to pass all its properties. Can someone explain why the excludeScrollbar property is necessary? Check out the code here! DatePicker ...

Navigating deprecated CanActivate in Angular-15 with Auth Guard

Authentication Guard: import { Injectable, inject } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivate, CanActivateFn, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; import { Observable } from 'rxjs& ...

Guidelines for transmitting form information to a web API using Angular

I am currently working on an Angular 6 project where I have a form and a table that retrieves data from a web API. I want to know if it's possible to send the form data to that web API. Here is the code snippet that I have so far: HTML Form: &l ...

Result of Mongodb aggregation operation

I've created a Property controller : //Retrieve Properties By Type const getPropertiesByType = async (req: Request, res: Response) => { const { cities, type } = req.query; const citiesArr = typeof cities === 'string' ? cities.spli ...

Angular's Spanning Powers

How can I make a button call different methods when "Select" or "Change" is clicked? <button type="button" class="btn btn-default" *ngIf="!edit" class="btn btn-default"> <span *ngIf="isNullOrUndefined(class?.classForeignId)">Select</spa ...