Are TypeScript conditional types and constrained generics mapped in different ways?

I can't seem to figure out why the code snippet below is behaving this way:

type MapOverString<T extends string> = { [K in T]: K };

type IfStringMapOverIt<T> = T extends string ? MapOverString<T> : never;

type ThisWorks = MapOverString<'a'>;
// { a: 'a' }

type ThisAlsoWorks = IfStringMapOverIt<'a'>;
// { a: 'a' }

type Union = 'a' | 'b' | 'c';

type ThisWorksToo = MapOverString<Union>;
// { a: 'a', b: 'b', c: 'c' }

type ThisDoesnt = IfStringMapOverIt<Union>;
// MapOverString<'a'> | MapOverString<'b'> | MapOverString<'c'>

Playground link

There must be something I'm missing because it seems like MapOverString and IfStringMapOverIt should work the same.

Overall, I am using a combination of string literals and generics to iterate through different configuration types. For instance, if you need StringConfig<T> set up with options 'a' | 'b' | 'c':

type ConfigMap<T> = T extends number
  ? NumberConfig
  : T extends string
    ? StringConfig<T>
    : never

type MyConfig = ConfigMap<'a' | 'b' | 'c'> // so many sad faces

Could someone provide some insight? What's really happening here?

Answer №1

This example illustrates the distribution property of conditional types. When a condition is placed over a naked type parameter, it triggers a specific behavior, as seen in T extends string. Other variations such as T extend T, T extends any, or T extends unknown are also used to invoke distribution.

To learn more about distributive conditional types, refer to the handbook

If you want to disable distribution, you can use a condition involving tuples like [T] extends [string]. This approach eliminates distribution since the type parameter is no longer "naked".

type StringConfig<T extends string> = { [K in T]: K };
type NumberConfig ={}

type ConfigMap<T> = [T] extends [number]
  ? NumberConfig
  : [T] extends [string]
    ? StringConfig<T>
    : never

export type MyConfig = ConfigMap<'a' | 'b' | 'c'> // so many sad faces
let x:MyConfig = {
  a:'a',
  b:'b',
  c: 'c'
}

Playground Link

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

There seems to be an issue with my React application that was built using Webpack 5 and compiled with TypeScript. The @tailwind directive is not functioning properly in the browser, and

As I embark on creating a fresh react application using Webpack 5, Tailwind CSS, and Typescript, I find myself at a crossroads. Despite piecing together various tutorials, I am struggling to configure the postcss-loader for Tailwind. While traditional .css ...

Modify the JSON file without using a library

I am dealing with a file called test.json Here is what it contains: [ { "data_on": { "vals_e": "", "vals_o": "" }, "data_off": { "vals_d": "" ...

Interactive Timeline - Adjust color on click

I am having trouble implementing a timeline and changing the color when clicking on different states. Can anyone help me figure out what the issue is or how I can achieve this? I intended to give each state a different color, but sadly, it didn't wor ...

Using AWS CDK to trigger an SMS notification when a Lambda function identifies updates in a URL

I am working on a CDK application that utilizes a Lambda function to monitor changes in the content of a website. My goal is to trigger an SMS notification to a mobile number whenever a change is detected. Within my AWS CDK project, I have set up a stack ...

Why does typescript allow the invocation of functions with an incorrect interface type?

Let's look at the scenario presented below. Notice that when func is called with an argument of type One, Typescript does not report any errors - what could be the reason behind this? export interface One { common?: string one?: string; } ex ...

How can I stop one mocha it function's Typescript class scaffolding/setup from disrupting another?

Currently, I am working on writing TypeScript tests in Mocha and have defined a test scenario as follows: describe("IsDefined", () => { it("should work correctly ...", () => { class Test { @IsDefined() p1: String = ""; ...

Angular component can receive object key-value pairs as separate arguments

Let's say I am working with a component that requires three separate inputs, named input1, input2, and input3. export class exampleComponent { @Input() input1:string; @Input() input2:string; @Input() input3:string; .... ... } In the parent ...

Struggling to retrieve local JSON file in Angular 5 - facing relentless 404 error

I've scoured every available article and post on this topic, yet I am still unable to pinpoint where I am making a mistake with this seemingly simple task. (Particularly, following this example.) It seems like I am missing something obvious, but after ...

employing constructor objects within classes

I am attempting to utilize a class with a constructor object inside another class. How should I properly invoke this class? For example, how can I use Class 1 within Class 2? Below is an instance where an object is being created from a response obtained f ...

Injecting a service into a base class in Angular, but not in the sub-class, then utilizing the parent service in the sub-

When working with Angular 7 and Typescript, I have a base class that relies on multiple services and has around 40 subclasses. Instead of adding these services to each subclass constructor and passing them to super(), I am looking for a more efficient wa ...

Building an Angular 4 app featuring a customized default landing page and URL parameters functionality

I am in the process of developing a web portal that will be embedded within an iFrame. Below is the code snippet I am using to set up the portal: Routes Configuration const routes: Routes = [ { path: '', redirectTo: '/dash ...

Troubleshooting: Imported Variable in Angular 2+ Throwing Module Not Found Error

During my testing process, I encountered an issue when trying to require a .json file with data to perform checks on. Despite passing the string indicating where to find the file into the require function, it seems to be unsuccessful... Success: const da ...

How to associate an object with a component in Angular2/TypeScript using HTTP

I am currently working on displaying a list of item names retrieved from a REST API. By utilizing the map function on the response observable and subscribing to it, I was able to obtain the parsed items object. Now, my challenge is how to bind this object ...

Calculate the total value of an object using Typescript

Here is the data that I have: [Products, Products, Products, Products] 0: Products {product_id: "1", Product_type_id: "11", Subtotal:450, …} 1: Products {product_id: "2", Product_type_id: "22", Subtotal:550, …} 2: Products {product_id: ...

The call to Contentful's getAsset function resulted in an undefined value being

I am facing a challenge while trying to fetch an asset, specifically an image, from Contentful and display it in my Angular application. Despite seeing the images in the Network log, I keep encountering an issue where the console.log outputs undefined. Any ...

Zoom in and out on a WebGL canvas created using Three.js using the mouse scroll wheel

Currently delving into the world of WebGL (Three.js) to explore the possibilities of rendering 3D scenes within an Angular app. One challenge I'm facing is finding a way to make the mousewheel events zoom in and out of the canvas specifically, rather ...

Tips for deleting multiple values from an array in Angular 2

When choosing multiple ion-items from a list, I am looking to eliminate identical items from an array. HTML Code <div class="ion-item optionalItem"> <button class="ion-button" ion-item *ngFor="let configop of interiorOption" (click)="itemSel ...

Having trouble navigating using router.navigate in the nativescript-tab-navigation template?

I have everything properly configured in my routes and no errors are popping up. When the initial tab is loaded, I am attempting to automatically switch to the second tab based on whether the user is logged in or not. tabs-routing.module.ts file: import ...

Encountering HttpErrorResponse when sending a Delete request in Angular

I need help troubleshooting an issue with sending a Delete request from my movie.component.ts file to delete a review. Unfortunately, I keep receiving the dreaded HttpErrorResponse error. Can anyone pinpoint where I may be making a mistake? Take a look at ...

Angular provides a convenient way to call an API on key press. Let's delve

How can I trigger an API call in Angular when a user clicks on a textbox during a keypress event? I am encountering an error with the debounce method that says Cannot read property 'valueChanges' of undefined app.component.ts ngOnInit() { t ...