Can we limit a generic type to only accept a subset of keyof values in TypeScript?

In the latest version (2.1) of TypeScript, I have the ability to restrict a method argument in a generic class to be a property of the generic type.

class Foo<TEntity extends {[key:string]:any}> {
    public bar<K extends keyof TEntity>(key:K, value:TEntity[K]) { }
}

Now my question is, can we further constrain the key part so that it belongs to a subset where the value of the key is of a specific type?

I have provided pseudocode for what I am looking for below.

class Foo<TEntity extends {[key:string]:any}> {
    public updateText<K extends keyof TEntity where TEntity[K] extends string>(key:K, value:any) {
        this.model[key] = this.convertToText(value);
    }
}

UPDATE

To clarify things, I have included a more comprehensive example to illustrate what I am trying to accomplish.

type object = { [key: string]: any };

class Form<T extends object> {
    private values: Partial<T> = {} as T;

    protected convert<K extends keyof T>(key: K, input: any, converter: (value: any) => T[K])
    {
        this.values[key] = converter(input);
    }

    protected convertText<K extends keyof T>(key: K, input: any)
    {
        this.values[key] = this.convert(key, input, this.stringConverter);
    }

    private stringConverter(value: any): string
    {
        return String(value);
    }
}

See Demo on typescriptlang.org

The issue with convertText arises when it says that

Type 'string' is not assignable to type 'T[K]'
.

For instance:

interface Foo {
    s: string
    n: number
}

The compiler determines that this will work:

this.convert('s', 123, v => String(v));

whereas this will not:

this.convert('n', 123, v => String(v));

My goal is to limit the convertText method to keys where the value is of type string in order to enforce type safety on the key parameter.

Answer №1

Here is an example that demonstrates how to ensure that the value of T[P] is always a string.

function convertText<T extends {[key in P]: string }, P extends keyof T>(data: T, field: P & keyof T) {
    // ...
}

The concept behind this implementation is to restrict the type of T to only include the fields specified in P, allowing you to explicitly define the desired type, which in this case is a string.

Let's test it out:

let obj = { foo: 'lorem', bar: 2 };
convertText(obj, 'foo');
convertText(obj, 'bar'); // This will result in an error: Type 'number' is not assignable to type '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

The boolean validation function appears to be malfunctioning in the NodeJS environment

I am currently working on developing the node js API and I am fetching data using a URL query. get_posts_default?pageId=ge4JqBn9F0srzHnVFHmh&asking_post=false&asking_responce=false&maxSort=-1&minSort=-1&limit=20 This function is respo ...

Exceed the capacity of a React component

Imagine having a React component that can render either a <button>, an <a>, or a React Router <Link> based on different props passed to it. Is it possible to overload this component in order to accept the correct props for each scenario? ...

Tips for configuring environment variables across multiple test files within Jenkins

In my random.test.ts file I am utilizing an environment variable: test.beforeAll(async () => { new testBase.BaseTest(page).login(process.env.EMAIL, process.env.PASSWORD); }) I want to execute my tests using Jenkins, but I am unsure of how to pass m ...

The error message "TypeError: this.subQuery is not a function" is displayed

Whenever I execute the tests using jest, I consistently encounter the error message TypeError: this.subQuery is not a function pointing to a specific line in the testModelDb.test.ts file. In the tests/jest.setup.ts file: import 'reflect-metadata&apos ...

What is the best way to reset an imported file with each test in viTest?

I'm having trouble resetting an imported file completely after each test. I believe that using vi.mock should mimic the original contents of my imported file, but it doesn't seem to be working when I try to modify the file during the tests. Here ...

Ways to retrieve a value from a JavaScript function without using the return statement

I wrote a Javascript method as follows: function ServerSideDatasource(server) { return { getRows: function (params) { var response = server.getData(params.request).then((res) => { var result = { success: true, ...

Executing a child component function once the parent component data is loaded in Angular 5

In my project, I have a parent component called program-page.component where I am invoking a function to fetch some data. ngOnInit() { this.getProgress(); } getFirstProgramItem() { this._contentfulService.getProgramItem(4, 1) .then((programItem) = ...

Unexpected JSON end causes issue with DELETE request in Next.js version 13

I am currently working on a web app using Next 13 and I have a route.ts file located in the api folder. This file contains two different methods, POST and DELETE. While both methods successfully receive the request, I am facing an issue with JSON parsing ...

Using multer to transfer variables to next handler

Utilizing multer for image uploads involves a specific configuration. Here is an example of how to set up multer: import multer from "multer"; import * as mime from "mime-types"; import path from "path"; export const storage = multer.diskStorage({ dest ...

What steps should I take to create a TypeScript generic class that is limited to only accepting types that are arrays of objects?

I'm working on creating a sample of a generic class in TypeScript. My goal is to have a generic class named RecordsProcessor that is limited to only accept types that are arrays of objects. If I try to pass a number to the constructor, TypeScript cor ...

Guide on simulating rxjs/Websocket in angular for performing Unit Testing

I have developed a service that manages websocket communication with a server and I am looking to create unit tests for it. However, I am facing challenges in mocking rxjs/Websocket. While searching for a solution, I came across a similar question here, b ...

Is it considered poor practice to specify the type explicitly when it can be easily inferred by Tslint?

When using VSCode, the linter tslint may raise an issue when the following code is added with a specific type: serverId: number = 10; This will trigger the following message: [tslint] Type number trivially inferred from a number literal, remove type ...

What is preventing this from being a function?

It appears that the authenticationProvider is missing for some reason. @autoinject() export class ProviderManager implements AuthenticationManager { constructor( private container: Container ){ } public authenticate( creds: Credentials ): Promis ...

Create a three-dimensional tree array in Typescript/Javascript by transforming a flat array

Received data is structured as follows: const worldMap = [ { "name": "Germany", "parentId": null, "type": "Country", "value": "country:unique:key:1234", "id&qu ...

Exploring the attributes of optional features

Dealing with optional properties can be quite tedious. Consider the object test1 in TypeScript: interface Test { a?: { b?: { c?: { d?: string } } }; } const test1: Test = { a: { b: { c: { d: 'e' } } } }; Handling the absence of each proper ...

Grid Layout with truncation in two dimensions

In my development using Material UI v5 for layouting, I've encountered an issue with truncating a string within a 2-dimensional Grid layout, specifically within a Dialog box. I am trying to create a file upload component with the desired layout shown ...

What is the process of programmatically sorting a column in a Material UI DataGrid?

Hey there! I'm currently working on a DataGrid that has a column with a custom header, specifically a Select option. My goal is to have the column sorted in descending order every time a user selects an option from the dropdown menu. renderHeader: (pa ...

Having trouble with importing files from a different folder in a React Typescript project

I have a specific folder arrangement set up https://i.sstatic.net/GFOYv.png My goal is to bring both MessageList.tsx and MessageSent.tsx into my Chat.tsx file // Chat.tsx import React from 'react' import {MessageList, MessageSent} from "./ ...

Tips on creating an editable table row in Angular to easily update values

I'm currently developing an Angular application which is meant to extract data from an excel sheet and exhibit it in a table upon upload. I have incorporated an edit link beneath one column for the purpose of editing the row data; once you click on ed ...

Having trouble loading JSON file contents into an array in Typescript

I am struggling to load my API keys from a JSON file named api.json. The file structure is as follows: { "service1": ["apikey1", "apikey2"], "service2": ["apikey1", "apikey2"] } In order to manag ...