Why isn't the constraint satisfied by this recursive map type in Typescript?

type CustomRecursiveMap<
    X extends Record<string, unknown>,
    Y extends Record<string, unknown>
> = {
    [M in keyof X]: M extends keyof Y
        ? X[M] extends Record<string, unknown>
            ? Y[M] extends Record<string, unknown>
                ? CustomRecursiveMap<X[M], Y[M]>
                : never
            : never
        : never
}

https://i.stack.imgur.com/r3UWB.png

playground

why X[M] does not comply with the constraint? Even after confirming it with

X[M] extends Record<string, unknown>
, how does this happen?

Answer №1

The reason for this issue is a bug in TypeScript, which can be found at microsoft/TypeScript#45651. It seems that in versions TS4.5 and earlier, performing a conditional type check on an indexed access types may not always properly constrain the type in the true branch. Fortunately, this bug has been resolved in microsoft/TypeScript#47791, which will be included in the upcoming TS4.6 release. Users can already use the release candidate version labeled as 4.6.1-rc:

type RecursiveMap<
    A extends Record<string, unknown>,
    B extends Record<string, unknown>
> = {
    [K in keyof A]: K extends keyof B
        ? A[K] extends Record<string, unknown>
            ? B[K] extends Record<string, unknown>
                ? RecursiveMap<A[K], B[K]> // okay
                : never
            : never
        : never
}

Playground link to 4.6.1-rc

If users cannot wait for the fix, they can apply a workaround using the Extract<T, U> utility type in an alternative manner. By substituting U with Extract<T, U>, where type T can be assigned to type

U</code even if the compiler doesn't recognize it correctly, the issue can be corrected. As long as the assumptions about <code>T
and U are accurate, later evaluation of Extract<T, U> will result in T (however, incorrect assumptions could lead to something closer to T & U, potentially resulting in never, so caution is advised):

type RecursiveMap<
    A extends Record<string, unknown>,
    B extends Record<string, unknown>
    > = {
        [K in keyof A]: K extends keyof B
        ? A[K] extends Record<string, unknown>
        ? B[K] extends Record<string, unknown>
        ? RecursiveMap<
            Extract<A[K], Record<string, unknown>>,
            Extract<B[K], Record<string, unknown>>
        >
        : never
        : never
        : never
    }

Playground link to code

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

Cannot assign an array of Typescript type Never[] to an array of objects

Creating an object array can sometimes lead to errors, let's take a look at an example: objectExample[a].push({ id: john, detail: true }); objectExample[a].push({ id: james, detail: false}); const objectExample = { a = [ { id: john, detail: true} ...

"Exploring the method to navigate through a nested Firebase collection linked to its parent collection

I have a forum application in development where users can ask questions and receive answers, each answer having its own 'like' feature. I am trying to access the 'likes' subcollection when viewing an answer, but I am unsure of how to do ...

Error429 was received from a GET request made to the Imgur API

Encountering a Request failed with status code 429 error from the Imgur API despite using a new Client_ID that hasn't been used before, Here is my Api.ts: const imgurClientId = process.env.NEXT_PUBLIC_Client_ID const BASE = "https://api.imgur. ...

Unable to locate youtube.ts file in the Angular 2 project for the YoutubeAPI integration

I've been using a youtube.d.ts file from the DefinitelyTyped project. It functions perfectly in my WebStorm while I'm editing, but once I try to run it, I encounter a 404 error stating that typings/youtube.js is not found. This file doesn't ...

How to verify if an unknown-type variable in TypeScript contains a specific property

When using typescript with relay, the props passed down are of type unknown. Despite my efforts, I am unable to persuade the compiler that it can have some properties without encountering an error: <QueryRenderer environment={environment} query={te ...

What is the best way to verify if an object is an instance of a particular class using Jasmine testing?

In the process of developing an Angular application, I am currently working on testing a component using this guide. My goal is to ensure that when my ngOnInit() method is called, it correctly initializes my foos property with an array of Foo objects based ...

Comparing TypeScript and C++ in terms of defining class reference member variables

class B; class A { A(B b_) : b{b_} {} B &b; }; In C++, it is possible to have a reference member variable like 'b' in class A. Can the same be achieved in TypeScript? Alternatively, is there a specific method to accomplish this in ...

Obtaining the component instance ('this') from a template

Imagine we are in a situation where we need to connect a child component property to the component instance itself within a template: <child-component parent="???"></child-component1> Is there a solution for this without having to create a sp ...

There seems to be a syntax error in the regular expression used in Angular TypeScript

I've encountered an error and I'm struggling to identify the syntax issue. core.mjs:6495 ERROR SyntaxError: Invalid regular expression: /https://graph.microsoft.com/v1.0/communications/callRecords/getPstnCalls(fromDateTime=2020-01-30,toDateTime ...

How can a button click function be triggered in another component?

These are the three components in my dashboard.html <top-nav></top-nav> <sidebar-cmp></sidebar-cmp> <section class="main-container" [ngClass]="{sidebarPushRight: isActive}"> <router-outlet></router-outlet> & ...

Navigating through Dynamic URL Parameters with RouterLink in an Angular Application

Within my Angular 2 application, there exists a tab section where users can choose from a collection of separate yet contextually connected components. By clicking on one of these links, the corresponding component is loaded based on the routerLink definit ...

Create a mechanism in the API to ensure that only positive values greater than or equal to 0 are accepted

My goal is to process the API result and filter out any values less than 0. I've attempted to implement this feature, but so far without success: private handleChart(data: Object): void { const series = []; for (const [key, value] of Object.e ...

Locate a class using an interface

Consider the code snippet below: interface FirstInterface {} interface SecondInterface {} interface ThirdInterface {} class TheClass { constructor(howdy: FirstInterface) {} } class Foo implements FirstInterface {} class Bar implements SecondInterface ...

TypeScript(error:2532): object may be undefined despite null or undefined check

Currently, I am developing a Discord-bot using TypeScript which includes a command to retrieve information from an airport. To do this, users only need to provide the 4-character ICAO code that identifies the specific airport. However, due to potential use ...

Creating interactive forms - Incorporating dynamic checkbox options within the expansion panel element

Recently, I developed a basic movie list app with a checkbox list for genre filtering. Initially, I managed to achieve the desired functionality without using reactive forms. However, I am now exploring implementing the same functionality using reactive ...

Encountering an issue with PrimeNG's <p-calendar> component: the error message "date

I encountered an issue resulting in the following error message: core.es5.js:1020 ERROR Error: Uncaught (in promise): TypeError: date.getMonth is not a function TypeError: date.getMonth is not a function This error occurs whenever I attempt to implement ...

What purpose does the # symbol serve in Angular when interpolating values?

Here is an example provided from the following link: https://angular.io/guide/lifecycle-hooks export class PeekABoo implements OnInit { constructor(private logger: LoggerService) { } // Implementing OnInit's `ngOnInit` method ngOnInit() { this ...

Retrieving the Object value in Primeng p-dropdown when there is a change in selection

In my p-dropdown, I am trying to extract the selected value. <p-dropdown optionLabel="name" [options]="things" placeholder="Select Thing" [(ngModel)]="input" (onChange)="getValue(input)"></p-dropdown> typescript: //each lin ...

What is the reason for the function to return 'undefined' when the variable already holds the accurate result?

I have created a function that aims to calculate the digital root of a given number. Despite my efforts, I am encountering an issue where this function consistently returns undefined, even though the variable does hold the correct result. Can you help me ...

What are some ways to enhance this TypeScript code using Functional Programming with the FP-TS library?

i am struggling to write some typescript code using fp-ts Below are the tasks that i want the algorithm to carry out: Once a path is received from the command line, it should check if the given path exists search for all files in the directory and locat ...