What causes the behavior discrepancy in Typescript Union + Mapped Types when used with and without Generics?

I am a novice in the realm of generics. Although the code snippets for "w0" and "w1" appear to be identical, they actually have different purposes and types.

Could someone shed light on why they are distinct from each other, and also provide guidance on achieving the opposite type interchangeably?

Despite extensively searching through various documents, I have yet to come across a comprehensive explanation. Does anyone know of a resource that delves into the underlying principles behind their varied types?

Is there a way to achieve

Calc<BaseA> | Calc<BaseB>
without explicitly invoking Calc or Generics?

How can I create

{ type: "A" | "B"; flag: number; }
utilizing the power of Generics?

type BaseA = {
  type: 'A'
  name: string
  flag: number
}

type BaseB = {
  type: 'B'
  id: number
  flag: number
}

type Base = BaseA | BaseB

type w0 = {
  [k in keyof Base]: Base[k]
}
/*
w0 = {
  type: "A" | "B";
  flag: number;
}
*/
type Calc<W> = {
  [k in keyof W]: W[k]
}
type w1 = Calc<Base>
/*
w1 = Calc<BaseA> | Calc<BaseB>
*/
type z0 = Exclude<w0,BaseA>
// z0 = w0
type z1 = Exclude<w1,BaseA>
// z1 = BaseB

Answer №1

The initial issue presents a straightforward solution:

type Q1 = {
  [B in Base as B["type"]]: {
    [K2 in keyof B]: B[K2]
  }
}[Base["type"]]

// type Q1 = {
//     type: 'A';
//     name: string;
//     flag: number;
// } | {
//     type: 'B';
//     id: number;
//     flag: number;
// }

We iterate through each element in Base and utilize the type key as the key for the resulting type. Within each element, we can traverse the keys of the element. Ultimately, we index this type with Base["type"] to obtain the union.

You have the option to substitute the inner mapping with Calc.

type Q1 = {
  [B in Base as B["type"]]: Calc<B>
}[Base["type"]]

// type Q1 = Calc<BaseA> | Calc<BaseB>

The subsequent problem is slightly more complex. It is induced by Distributive Conditional Types. I discovered that distributivity could only be deactivated using this tactic:

type Calc2<W extends [any]> = {
  [k in keyof W[0]]: W[0][k] 
}

type Q2 = Calc2<[Base]>

// type Q2 = {
//     type: "A" | "B";
//     flag: number;
// }

Playground

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

Changing the mouse cursor dynamically with Angular programming

What is the recommended approach for changing the mouse cursor programmatically in Angular? For instance: HTML: <div [style.cursor]="cursorStyle">Content goes here</div> or <div [ngStyle]="{ 'cursor': cursorStyle ...

Signatures of indices

I'm struggling with defining types for the following code: type Language = 'en' | 'nl'; interface CacheObject { [key: string | number | Language]: string; } const cache: CacheObject = {}; export const init = (dir: string): Pr ...

TypeScript disregards interface method argument types

Unexpectedly, the code below compiles without any errors (using tsc 3.9.5): interface IDateHandler { handleDate: (Date) => void; } let dateHandler: IDateHandler = { handleDate: (d: Date) => {}, }; dateHandler.handleDate([1, 2, 3]); Even more s ...

Retrieve the IDs of all children and grandchildren within a parent ID using Typescript

If I were to have an array of objects containing hierarchical data: const data = [ { groupId: 1, parentGroupId: null }, { groupId: 2, parentGroupId: 1 }, { groupId: 3, parentGroupId: 1 }, { groupId: 4, parentGroupId: null }, { groupId: 5, parentG ...

Determine the accurate data types while transforming an array into an object using a specific key

I have an array of elements, each with a unique category string property. I aim to transform this into a structure where the category serves as the key and the original element is the value. To tackle this, I first verify that I possess a correctly typed ...

Guide on creating several TypeScript interfaces that share identical type structures

export interface UserFailureResponse { statusCode: number statusMessage: string } export interface UserCreateResponse { statusCode: number statusMessage: string } export interface AuthCheckResponse { statusCode: number statusMessa ...

Upgrading my loop React component from vanilla JavaScript to TypeScript for improved efficiency and functionality

After seeking assistance from Stack Overflow, I successfully created a loop in React using a functional component that works as intended. However, I am encountering errors while trying to refactor the loop to TypeScript. The code for my DetailedProduct c ...

Issue with the proper functionality of the this.formGroup.updateValueAndValidity() method in Angular 6

Currently, I am facing an issue where I need to add or remove validators in a formGroup's controls based on certain conditions. When I try to update the validators using `formGroup.updateValueAndValidity()` for the entire form, it does not seem to wor ...

I am facing difficulty in deploying my Next.js app with Firestore

Having trouble deploying my application using npm run build, Vercel, or another service. It works fine locally without showing any errors. I am using Firebase, Next.js, and TypeScript. The issue seems to be related to getStaticProps. When requesting data, ...

Angular is unable to modify the value of 'name' since it is either a constant or a property that cannot be modified

I am encountering an error that says "Cannot assign to 'name' because it is a constant or a read-only property" when trying to send data to the API. Does anyone know how I can solve this issue? Thank you. onSubmit() { const name = this.backU ...

Challenge with Dependency Injection in the Handlers of NestJS CQRS repositories

As a newcomer to nodejs, I am currently delving into the implementation of NestJS's CQRS 'recipe'. In my service, I have a Request scoped with the injection of QueryBus: @Injectable({scope: Scope.REQUEST}) export class CustomerService { co ...

Angular - Bootstrap modal displays as a standalone element rather than a dialog box

Currently working on my initial Angular project, I am attempting to incorporate a dialog that prompts for confirmation before deleting an item. Utilizing ng-bootstrap, I referred to the examples in the documentation as my starting reference. The issue I a ...

CoursesComponent does not contain a Directive annotation

I have been following a tutorial online at this link: https://www.youtube.com/watch?v=_-CD_5YhJTA Unfortunately, I keep encountering the following error message: EXCEPTION: No Directive annotation found on CoursesComponent Here is an excerpt from my a ...

"Implementing a call and waiting at intervals by utilizing the subscribe function in Angular 6

In my code, I have a method that is called every 10000 times. Now, I want to modify this so that the function getAllNotificationsActed0() is invoked every 10 seconds. If the data does not arrive within this interval, I do not want the function to be called ...

Transform the API response into a map in Angular version 16

When I receive a JSON response from a Java server, the structure looks like this: { "summary": { }, "runs": { "key_1": { "object_1": { }, "object_2": { ...

Issue with readonly is preventing the ability to alter the font color of the input

I need to change the font color of a disabled input. When it is disabled, it appears gray and I want it to be black instead. I attempted to use readonly but that did not have the desired effect, and now the input is showing [object Object]. Below is my HTM ...

The specified file path '.../node_modules/@nomicfoundation/hardhat-core/src' could not be located

I have successfully set up a TypeScript hardhat project, but I encountered an issue in /***/node_modules/@nomicfoundation/hardhat-chai-matchers/src/tsconfig.json: { "extends": "../../../config/typescript/tsconfig.json", "compil ...

What is the best way to loop through an array that contains a custom data type

When I declared the type: export interface Type{ id: number; name: string; } I attempted to iterate over an array of this type: for(var t of types) // types = Type[] { console.log(t.id); } However, I encountered the following error message: ...

The type 'number' cannot be assigned to the type 'Element'

Currently, I am developing a custom hook called useArray in React with TypeScript. This hook handles array methods such as push, update, remove, etc. It works perfectly fine in JavaScript, but encounters errors in TypeScript. Below is the snippet of code f ...

Integrating JavaScript functions into TypeScript

When converting a JavaScript function to TypeScript, I encountered an issue. The function works fine in JS but in TS, I receive the following error: [ts] Cannot find name 'PasscodeAuth'. Did you mean 'passcodeAuth'? function passco ...