Using TypeScript conditional types with extends keyof allows for checking against specific keys, but it does not grant the ability

In TypeScript, I created custom types using template literal types to dynamically type the getters and setters of fields. The types are structured like this (Playground Link):

export type Getters<T> = {
  [K in `get${Capitalize<keyof T & string>}`]: K extends `get${infer S}` ? (
    S extends keyof T ? () => T[S] : (
      Uncapitalize<S> extends keyof T ? () => T[Uncapitalize<S>] : never
    )
  ) : never;
};

export type Setters<T> = {
  [K in `set${Capitalize<keyof T & string>}`]: K extends `set${infer S}` ? (
    S extends keyof T ? (newValue: T[S]) => void : (
      Uncapitalize<S> extends keyof T ? (newValue: T[Uncapitalize<S>]) => void : never
//                                                 ^^^^^^^^^^^^^^^^^^
    )
  ) : never;
};

However, I encountered a type error on line 12 (highlighted above), within the Setters<T> type definition. The error states:

Type 'Uncapitalize' cannot be used to index type 'T'.

Despite checking that

Uncapitalize<S> extends keyof T
just before, I am puzzled as to why there is still a type error. I am also wondering why it worked without errors a few lines above, in the Getters<T> type definition. This leads me to question the difference between them and why one is facing an error while the other is not.

EDIT (I forgot to mention this initially): I am aware that I can duplicate the type check inside the parameter's type (as suggested by @AntonKastritskiy in his answer), but I prefer to avoid this repetition and seek a deeper understanding of the issue.


Perhaps there is a more efficient way to achieve this (especially in terms of preserving a reference to the current keyof T during iteration, rather than breaking down the getter/setter names). While I attempted this as a personal challenge, I am now open to suggestions for improvement, if any 😊

Answer â„–1

Unfortunately, I don't have the precise answer to your query. The issue appears to arise only when the type is utilized for a parameter, not the return type. One way to resolve the error is to implement an additional check to confirm that Uncapitalized is a keyof T in the parameters, like so:

export type Setters<T> = {
  [K in `set${Capitalize<keyof T & string>}`]: K extends `set${infer S}` ? (
    S extends keyof T ? (newValue: T[S]) => void : (
      Uncapitalize<S> extends keyof T ? (newValue: Uncapitalize<S> extends keyof T ? T[Uncapitalize<S>] : never) => void : never
    )
  ) : never;
};

PS: Let me highlight that despite the attempt in the code to accommodate scenarios where a field may start with an uppercase letter, it still does not cover cases where two fields differ solely by the casing of their initial letter. For instance:

type Foo = Setter<{a: number, A: string}> // {setA: string}

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

Solve the TypeScript path when using jest.mock

I am currently in the process of adding unit tests to a TypeScript project that utilizes compilerOptions.paths. My goal is to mock an import for testing purposes. However, I have encountered an issue where jest is unable to resolve the module to be mocked ...

Question from Student: Can a single function be created to manage all text fields, regardless of the number of fields present?

In my SPFX project using React, TypeScript, and Office UI Fabric, I've noticed that I'm creating separate functions for each text field in a form. Is there a way to create a single function that can handle multiple similar fields, but still maint ...

Issue with dynamic imports and lazy-loading module loadChildren in Jhipster on Angular 8, not functioning as expected

When utilizing dynamic import, it is necessary to modify the tsconfig.json file in order to specify the target module as esnext. ./src/main/webapp/app/app-routing.module.ts 14:40 Module parse failed: Unexpected token (14:40) File was processed with these ...

The incorrect initial state is causing issues in the Zustand state management on the Next.js server side

While utilizing zustand as a global state manager, I encountered an issue where the persisted states were not being logged correctly in the server side of nextjs pages. The log would only show the default values (which are null) and not the updated state v ...

Encountering an error in Angular 2: "date.getMonth is not a function

I am currently utilizing the Angular-2-datepicker in my project. Everything seems to be functioning properly, but whenever I attempt to set the [(date)] attribute, an error is thrown. An error stating that date.getMonth is not a function keeps popping u ...

Visual Studio is refusing to highlight my code properly, intellisense is failing to provide suggestions, and essential functions like go to definition are not functioning as expected

Due to a non-disclosure agreement, I am unable to share any code. However, I am experiencing an issue with Visual Studio not highlighting my code or allowing me to utilize its built-in tools. While I can rebuild the project, I cannot edit or access any fil ...

Is there a way to retrieve all potential string literals from an Array<>?

Can something similar be achieved in TypeScript? const options: Array<'Option1' | 'Option2' | 'Option3'> = []; // specify all available options: 'Option1' | 'Option2' | 'Option3' as show ...

Contrast between utilizing and publishing, demanding and bringing in within Express

I have recently started learning Typescript and Express. I have a simple exported function that looks like this: export function testFunction(req: any, res: any) { console.log(req.body); return res.status(200).send('OK'); }; And ...

Hide the FormArray in a Reactive Form if it is not populated or cannot be determined

As a newcomer to Angular 4, I've embarked on my first project and started learning the ropes. I have devised a Reactive Form to showcase a form structure that looks like this: Form (FormGroup) | --> aggLevels (FormArray) | --> ...

Instructions on how to dynamically show specific text within a reusable component by utilizing React and JavaScript

My goal is to conditionally display text in a reusable component using React and JavaScript. I have a Bar component that I use in multiple other components. In one particular ParentComponent, the requirement is to show limit/total instead of percentage va ...

When emitting an event multiple times in Angular, an error may occur where properties of undefined are unable to be read, particularly in relation to the "

I am encountering an issue with my event binding on a button, specifically (click)="onStart()". The problem arises when the event this.numEmitter is emitted for the first time in setInterval, after which I receive the error message ERROR TypeError: Cannot ...

Setting up a variable with a changing value

In a very specific scenario, the body of type varies based on the length_type attribute (as illustrated in the example). enum LengthTypeEnum { SELECT = 'SELECT', STATIC = 'STATIC', CONDITION = 'CONDITION', PERIOD = ...

"Error: imports are undefined" in the template for HTML5 boilerplate

After setting up an HTML5 Boilerplate project in WebStorm, I navigate to the localhost:8080/myproject/src URL to run it. Within the src folder, there is a js directory structured like this: libraries models place.model.ts place.model.js addr ...

Tips for troubleshooting the error "Cannot locate module mp3 file or its associated type declarations"

https://i.sstatic.net/q4x3m.png Seeking guidance on resolving the issue with finding module './audio/audio1.mp3' or its type declarations. I have already attempted using require('./audio/audio1.mp3'), but continue to encounter an error ...

Double invocation of ActivatedRoute.params.subscribe method observed

To extract URL parameters, I'm utilizing the ngOnInit() method where I've implemented the following snippet: this.activatedRoute.queryParams.subscribe(params => { console.log(params); // actual implementation here }); Yet, upon initi ...

Matching only the specified Records in an array of Typescript generic objects

Check out this demo: https://tsplay.dev/Nnavaw I am working with an array that has the following structure: Array<{ id?: string; text?: string; date?: Date; }> This conflicts with the current implementation: data: Array<Par ...

Dealing with conflicting tsconfig.json files: Utilizing CommonJS for Node.js and ES6 for React.js

Here is the current folder setup for my TypeScript files: ts_dev --client *components.tsx *tsconfig.json --server *server.ts *tsconfig.json --share *utility.ts The Node.js server needs to use commonjs modules, while the client side compone ...

Efficiently convert Map keys into a Set in Javascript without the need to copy and rebuild the set

Even though I am capable of const set = new Set(map.keys()) I don't want to have to rebuild the set. Additionally, I prefer not to create a duplicate set for the return value. The function responsible for returning this set should also have the abili ...

Repeatedly calling the subscription results in the dialogue opening twice due to the state mutation with Ngrx in Angular

Repeated Dialog Opening Due to Multiple Subscription Calls in Response to ngrx State Mutation Attempted to use takeUntil(loadingComplete) where loadingComplete = new BehaviorSubject(false) but it did not work within the logic. This is because the subsc ...

The type 'IContact[]' given does not match the expected type 'FetchContactsSuccessPayload' for the parameter

I've been diving into my project involving react redux-saga with TypeScript and I'm facing an issue with a type error within my saga file. This is my first experience with TypeScript. The error originates from the saga.ts file, specifically this ...