Creating Unique Local Types without Compatibility Issues in TypeScript

TypeScript Version: 3.8.2

Search Terms: map, over, utility, type, result, keys, extract

Code

The following helper function is functioning beautifully for me:

export type ComputeUnionValue<V extends AnyCodec[]> = {
  [i in Extract<keyof V, number>]: GetValue<V[i]>;
}[Extract<keyof V, number>];

I am attempting to simplify this helper, which incorporates Extract<keyof V, number> in two instances.

export type ComputeUnionValue<V extends AnyCodec[], E = Extract<keyof V, number>> = {
  [i in E]: GetValue<V[i]>;
}[E];

The use of E in [i in E] results in an error:

Type 'E' is not assignable to type 'string | number | symbol'.
  Type 'E' is not assignable to type 'symbol'.ts(2322)

Furthermore, the usage of V[i] leads to errors:

Type 'V[i]' does not meet the constraint 'AnyCodec'.
  Type 'V[E]' is not suitable for type 'Codec<VType, unknown>'.
    Type 'AnyCodec[][E]' is not compatible with type 'Codec<VType, unknown>'.ts(2344)

and

Type 'i' cannot be used as an index for type 'V'

I opted for using E = to include that new type within the scope without it being an optional argument. However, treating it as optional seems to impact the "guarantees", causing the compatibility issues mentioned above. Is there a way to define the E type––specific to this utility function––in a manner that eliminates any possibility of type incompatibility?

Answer №1

Although I have observed type parameters being used as 'local types variable', I personally do not consider it a wise practice because it exposes internal logic to the external world (as someone could easily pass in E instead of using the default). It also complicates things for users, making them differentiate between "real" and "local" parameters, which negatively impacts developer experience. (For your information: there was a proposal on GitHub to allow local types aliases, but it doesn't seem to have gained traction)

The reason why you are encountering an error is simply because = only sets a default value for a type parameter, but the parameter itself can be of ANY type, including ones that may not work in a mapped type.

The solution, although straightforward albeit slightly verbose, involves providing both a default value and constraint (using extends) for the type parameter:

export type ComputeUnionValue<V extends AnyCodec[], E extends Extract<keyof V, number> = Extract<keyof V, number>> = {
  [i in E]: GetValue<V[i]>;
}[E];

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

Tips for restricting keys when using a union as an indexer without demanding all of them

Trying to create a type that only allows certain keys from a union using the key in statement has resulted in all keys being required. How can I define this type so that not all values have to be present? const widgetOptions = ['option1', 'o ...

The Angular 5 keyup event is being triggered twice

My app is incredibly simple, just a basic hello world. To enhance its appearance, I incorporated bootstrap for the design and ng-bootstrap for the components. Within one of my TS files, you will find the following code: showMeTheKey(event: KeyboardEvent) ...

Creating type definitions for TypeScript (ts) involves defining the shape and

Having trouble using a library installed in node_modules/ within a typescript app? Here's a quick hack for you. Create a folder named after the module in typings/modules and add an index.d.ts file inside with the following content: declare module "li ...

What's the best way to determine the type of the enum literally?

I am working with an enum called RadCheck that looks like this: enum RadCheck { blank = 0, userDefined = 1, macAddress = 2, } const H = RadCheck[RadCheck.blank] Currently, the variable H is being recognized as a string, but I want it to be inferred ...

Ways to insert script tag in a React/JSX document?

private get mouseGestureSettingView() { const {selectedMenu} = this.state; return ( selectedMenu == 2 ? <script src="../../assets/js/extensions/mouse-gesture/options.js"></script> <div className={styles.settingForm}& ...

What could be causing Unity WebGL to malfunction on my React-based website?

I've been attempting to embed a Unity game into my website using React Unity WebGL. Despite following numerous tutorials and videos, the games are not appearing on my site (see images below). Can anyone provide insight into what I might be doing wrong ...

The gitlab CI is encountering an issue with the eslint warning

I am currently utilizing React with Typescript. I have configured eslint locally to treat unused variables as a warning. Oddly enough, when I execute npm run build on my local machine, it works perfectly fine. However, in Gitlab CI, I am encountering the ...

Optimizing Node Docker Build and Production Container Strategies

Currently, I am working on a Node project that utilizes MongoDB. In order to conduct automated testing, we have implemented the use of Mongo Memory Server. However, we have encountered an issue where Mongo Memory Server does not support Alpine, therefore ...

Is there a way to include two objects in an Angular2 post request?

This piece of code is giving me trouble: On the client side (using Angular 2) saveConfig(configType: ConfigTypes, gasConfigModel: GasConfigModel): any { console.info("sending post request"); let headers = new Headers({ 'Content-Type& ...

Manipulating classes in Angular based on array positions

Currently, I am working on developing a blog page with angular 5. In this setup, all blog posts are assigned the class: col-md-4 However, my goal is to give the newest blog post (the first one shown on the page) a different class: col-md-12. Below is th ...

Unleash the full power of Angular Components by enhancing them with injected

I am facing a challenge with handling the destruction event of an Angular component in my external module that provides a decorating function. I've run into issues trying to override the ngOnDestroy() method when it includes references to injected ser ...

What is the best way to upload and parse large files one line at a time with ExpressJS?

When dealing with uploading a large file to Express, I have successfully accessed the files object using the express-fileupload middleware. Here is an example of the files object: { myfile: { name: 'somelargefile.txt', data: <Buffer ...

Utilizing indexes to incorporate elements into an object array

I'm currently working on a project using Angular. I have an index coming from the HTML, and here is the code snippet: save(index){ //this method will be called on click of save button } In my component, I have an array structured like this: data = [{ ...

Retrieve the dimensions of the image in degrees from the component

I need to retrieve the dimensions of an image I uploaded along with its base64 code. Below is the code snippet I am using: Image img = new Image(); img.src = Selectedimage.src; Img.onload = function { this.width = img.width; this.height = img.height; } T ...

No gripes about incorrect typing when extending interfaces

I tried out the following code snippet here interface OnlyName { name: string } interface MyTest2 extends OnlyName { age: number } let test1: OnlyName; const setTest1 = (v: OnlyName) => { test1 = v console.log(test1) } let test2: My ...

Transform an array of objects into a two-dimensional array to organize elements by their identical ids in typescript

I have a collection of objects: arr1 = [{catid: 1, name: 'mango', category: 'fruit'}, {catid: 2, name: 'potato', category: 'veg'}, {catid: 3, name: 'chiken', category: 'nonveg'},{catid: 1, name: & ...

Convert the generic primitive type to a string

Hello, I am trying to create a function that can determine the primitive type of an array. However, I am facing an issue and haven't been able to find a solution that fits my problem. Below is the function I have written: export function isGenericType ...

Crafting a versatile type guard in TypeScript - step by step guide!

Issue with Generic Type Guard I'm facing a problem while trying to create a generic type guard for various Token types. The current implementation I have is as follows: export function isToken<T extends Token>(token: any): token is T { for (c ...

Stop the ability to "submit" inline edits in AG-Grid

Currently, I am attempting to implement an inline-editable table using Ag-Grid (v 17.0). However, I have encountered an issue where once I finish editing a row and press enter, the changes are immediately saved. Ideally, I would like the user to remain in ...

The error message in TypeScript is indicating that the property 'x' is not found in the type '{}', which is required for a computed key

Description of a Typescript fragment: enum Field { age, bugs, } interface Foo { number_age: number; number_bugs: number; } function createFoo():Foo { let obj = {}; let index = 0; for (let key in Field) { obj['numb ...