Prohibit the use of explicit type parameters or limit the union type parameters to enhance the safety of the types

When the getValues() function is called without explicit type parameters, the Typescript code functions correctly. However, calling it with explicit type parameters can result in errors (as seen in invocation

getValues<'a' | 'b' | 'c' >(store, 'a', 'b')
below).

type Store = Record<string, string>;

function getValues<T extends string>(
    store: Store, ...keys: T[]
): {[P in T]: string} {
    const values = {} as {[P in T]: string};

    keys.forEach((p) => {
        const value = store[p];
        if (value) {
            values[p] = value;
        } else {
            values[p] = 'no value';
        }
    });

    return values;
}


// Usage / requirements:    

const store: Store = {}; // assume it is filled with arbitrary values


// The following line infers values1 to be of type
// {a: string, b: string}, which is the desired behavior
const values1 = getValues(store, 'a', 'b');


// The next line does not compile, as intended
// Argument of type 'b' is not assignable to parameter of type 'a' | 'c'
const values2 = getValues<'a' | 'c'>(store, 'a', 'b');


// In the following line, values3 is inferred to be of type
// {a: string, b: string, c: string}, which is NOT the desired behavior
// It should ideally be forbidden by the compiler, similar to how values2 was handled
const values3 = getValues<'a' | 'b' | 'c' >(store, 'a', 'b');

// Now values3.c can be accessed, 
// although values3.c cannot have a value and will raise an error when used

Is there a way to prevent the caller from specifying explicit type parameters or any other method to enhance the type safety of this code without making the return type of the getValues function partial or its return type's properties optional?

I have a few ideas that I am unsure about their feasibility in Typescript:

Idea 1: Restrict providing explicit type parameters

Idea 2: Eliminate value arguments in getValues and access the provided type arguments at runtime

Idea 3: Mandate all provided type arguments to also be used as value arguments. This means the declaration of constant values3 above would trigger a compilation error.

I understand that the issue stems from the risky type assertion as {[P in T]: string}, so I am seeking ways to ensure type safety.

Answer №1

Dealing with the level of complexity necessary to enforce guarantees from the type system for such cases may not be worth the effort. It's important to consider the trade-off between complexity and the probability of users manually specifying overly broad type parameters. Instead of trying to make the compiler enforce this, it might be simpler to document the function (

/** hey, let the compiler infer T, thanks **/
) to inform users.


It's challenging to prevent explicit type parameters from being specified because even if achieved, type inference could still lead to wider types that are being avoided:

const aOrB = Math.random() < 0.5 ? "a" : "b";
getValues(store, aOrB); // {a: string, b: string} again

Runtime access to type parameters is impossible since they are erased along with the static type system during transpilation to JavaScript.


One potential solution discussed by TypeScript developers in handling similar issues with computed property keys involves a concept called Unionize. This method aims to produce {a: string} | {b: string} when uncertainty exists about the key being either "a" or "b".


Utilizing a type like Unionize alongside programmatic intersection can help create the desired output type for each passed-in key. The generic parameter should preferably be a rest tuple of key types rather than a union of key types:

// Types definitions...

The getValues() function implementation reflects these types:

// getValues function implementation...

To ensure correctness, manual specifications for type parameters must adhere to specific tuple requirements. Incorrect tuples will trigger an error, while unions within the tuple result in a union of object types as output.


This approach demonstrates how close one can get to achieving the correct output type, despite its complexity. While effective, there may be unexplored edge cases to consider. Documenting the original function for user awareness of compiler limitations might be a more practical recommendation.

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

Javascript operations for duplicating and altering arrays

In my Angular application, I am working with an array called subAgencies that is connected to a datasource. I need to implement 2-way binding on this array. Currently, I have a method in place where I copy the contents of the original array to a new one, ...

Implementing intelligent parameter type enforcement according to configuration settings

I can't decide on a name for this concept, so please be patient while I explain it. There are three configuration objects: const configA = { type: 'A' as const, getPath: (query: { foo: string }) => `/${query.foo}` } const config ...

Display a modal dialog using HttpInterceptor

@Injectable() export class MyInterceptor implements HttpInterceptor { intercept(req : HttpRequest<any>, next : HttpHandler) : Observable<HttpEvent<any>> { //display a modal dialog to wait for user response before proceeding with t ...

"Encountering a 'Module Not Found' error in Node.js after

Recently, I added a node-module to my project using the command: npm install typescript --save-dev However, when I tried running: tsc Windows displayed an error message indicating that "tsc" is an unknown command. Strangely, this issue does not occur o ...

What is the best way to merge arrays within two objects and combine them together?

I am facing an issue where I have multiple objects with the same properties and want to merge them based on a common key-value pair at the first level. Although I know about using the spread operator like this: const obj3 = {...obj1, ...obj2} The problem ...

A guide on implementing Angular-DataTable within a TypeScript project

Hey everyone, I'm currently working on a TypeScript and Angular application. To create a data table, I decided to use Angular-DataTable. After creating a sample application using it, I added the following code to my Controller: constructor(protecte ...

Issue with VS2017RC: TypeScript fails to produce JavaScript files

After updating to VS 2017, I noticed that modifications made to a typescript file no longer result in the generation of any javascript code, despite receiving a message indicating "outputs generated successfully" on the lower bar. I tested this issue on t ...

The issue with Angular 4 imports not refreshing in a .less file persists

Currently, I am in the process of developing a small Angular project that utilizes less for styling purposes. My intention is to separate the styling into distinct folders apart from the components and instead have a main import file - main.less. This fil ...

Is it possible to dynamically close the parent modal based on input from the child component?

As I follow a tutorial, I am working on importing the stripe function from two js files. The goal is to display my stripe payment in a modal. However, I am unsure how to close the modal once I receive a successful payment message in the child. Below are s ...

Ways to categorize items retrieved from an HTTP request to the backend in Angular

When making a call to the backend using this http request: this.StudentEnrollment.getRecordsById(list.value.split(/[\r\n]+/)).subscribe(values => { this.studentObject = values; }); The studentObject is structured as shown below: { recor ...

The Google Sign-in feature is unable to access the property 'load' due to being undefined

I'm currently working on implementing a Google Sign-in feature in an Angular application, but I'm encountering the following error: Cannot read property 'load' of undefined This was actually working perfectly just an hour ago, but n ...

Exception occurs when arrow function is replaced with an anonymous function

Currently, I am experimenting with the Angular Heroes Tutorial using Typescript. The code snippet below is functioning correctly while testing out the services: getHeroes() { this.heroService.getHeroes().then(heroes => this.heroes = heroes); } H ...

The guidelines specified in the root `.eslintrc.json` file of an NX workspace do not carry over to the project-level `.eslintrc.json` file

In my main .eslintrc.json file, I have set up some rules. This file contains: { "root": true, "ignorePatterns": ["**/*"], "plugins": ["@nrwl/nx", "react", "@typescript-eslint", &qu ...

Is it advisable to encapsulate my entire Express server within a TypeScript class?

After working extensively with nodeJs, I have decided to explore developing applications in Typescript. Recently, I came across various blogs (like this one) that recommend wrapping modules and the app's entry point in a class when creating a RESTful ...

Issues with the visibility of inlined styles within the Angular component library

After developing a custom library with components for an Angular 4 application, I encountered an issue where the CSS styling from the components was not being applied when using them in an application. Although the functionality worked fine, the visual asp ...

Ways of invoking a component method from a service in Angular 2

I have a concept for creating a unique service that is capable of interacting with one specific component. In my application, all other components should have the ability to call upon this service and then have it interact with the designated component in ...

Encountering a TS1005 error while trying to import types from a type definition file

Within my project, one of the libraries called parse5 is providing typing information in .d.ts files. The current syntax used to import types is causing several TypeScript errors during application runtime because TypeScript does not seem to recognize this ...

Tips for creating a mapped type in TypeScript that is based on an array

Is there a way to create a function with dynamic properties? function magic(...propertyNames:string[]): { ????? : any } { .... } Could the returned type have properties listed in propertyName? For instance: type ResultType = {alpha:any, bravo:any}; le ...

Transforming JSON keys in Angular

As a newcomer to angular and API integration, I am facing an issue with ngCharts in my project. The chart specifically requires the keys names in JSON to be "value" and "name", but the API I am using provides keys named "count" and "label". Is there a way ...

Modify the style of an element using a media query and Angular 4

Is there a way to update a class of an element based on the browser's width in my .ts file? matchMedia('(max-width: 400px)').addListener((mql => { if (mql.matches) { this.myclass = 'toggled'; } })); In the HTML, it shou ...