Exploring TypeScript: Determining the data type of an object key within the object

Apologies for the vague title, I'm struggling to articulate my problem which is probably why I can't find a solution!

Let me illustrate my issue with a snippet of code:

type Type<T> = {
  key: keyof T,
  doStuff: (value: T[typeof key]) => void
//                          ^^^
// TS2034: Cannot find name 'key'.
};

My intention here is quite clear (I hope). I've attempted to resolve this multiple times but always end up with a parameter that includes all available types.

const Test: Type<{ var1: string, var2: number }> = {
  key: 'var1',
  doStuff: (value) => {}
//          ^^^^^
// (parameter) value: string | number
};

I would greatly appreciate any assistance on this matter. If you require more information about my objective or what steps I've taken so far, please don't hesitate to ask!

Answer №1

In TypeScript, there are no predefined existential types, which means you cannot simply specify "I am looking for any key key from type T."

To achieve this functionality, you can make use of generics by defining the Type interface with two generic parameters T and K extends keyof T:

type Type<T, K extends keyof T> = {
  key: K,
  doStuff: (value: T[K]) => void
};

Then, you can create instances of Type like so:

const Test: Type<{ var1: string, var2: number }, "var1"> = {
  key: 'var1',
  doStuff: (value) => { } // value is inferred as string
}

This approach works, but if manually specifying the key "var1" bothers you, a workaround using currying can be implemented where one part is specified while the other is left to be inferred:

const typeFor = <T>() => <K extends keyof T>(type: Type<T, K>) => type;

// Manual specification of T
const typeForVar1StringVar2Number = typeFor<{ var1: string, var2: number }>();

// Inference of K
const Test2 = typeForVar1StringVar2Number({
  key: 'var1',
  doStuff: (value) => { } // value is inferred as string
});

If you require existential-like types due to having a union of literals in keyof T, distributive conditional types can be utilized to handle such cases:

type PossibleTypes<T> = keyof T extends infer K ? 
  K extends any ? Type<T, K> : never : never;

An array containing possible types can be created using the above definition:

type ArrayOfPossibleTypes<T> = Array<PossibleTypes<T>>
const asArrayOfPossibleTypes = <T>(arr: ArrayOfPossibleTypes<T>) => arr;
const testArray = asArrayOfPossibleTypes<{ var1: string, var2: number }>([
  {
    key: 'var1', doStuff(value) { /* value is string */ }
  }, {
    key: 'var2', doStuff(value) { /* value is number */ }
]
);

If all else fails, there is an implementation of existential types in TypeScript involving continuation passing, although it might be more complex than needed for your situation:

type ExistentialType<T> = <R>(f: <K extends keyof T>(x: Type<T, K>) => R) => R;

You can define and utilize an existential type as demonstrated below:

const exType: ExistentialType<{ var1: string, var2: number }> = 
  (f) => f({ key: 'var1', doStuff(value) { } });

The usage of existential types through continuation passing may be excessive for your requirements.

I hope these explanations provide clarity and assistance. Best of luck with your TypeScript endeavors!

Answer №2

In case you are okay with specifying the key within the type parameters:

export type CustomType<T, K extends keyof T> = {
    propertyKey: K, // optional
    performAction: (data: T[K]) => void
};

const Sample: CustomType<{ item1: string, item2: number }, "item1"> = {
    propertyKey: "item1", // optional
    performAction: (data): void => {
        // data is of 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

Incorporating TypeScript into a project originally developed in JavaScript

I'm considering using TypeScript to write code for a JavaScript project. I've come to appreciate the benefits of TypeScript and am especially interested in using it for our AngularJS 1.5 project, which we plan to migrate soon. As I'm new to ...

Transforming file location to base64 encoded format using TypeScript

I have the path of an image and need to convert it to base64 format, similar to this data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUg... function encodeImageToBase64(url, callback) { var xhr = new XMLHttpRequest(); xhr.onload = function() { va ...

405 we're sorry, but the POST method is not allowed on this page. This page does

I'm currently working on a small Form using the kit feature Actions. However, I'm facing an issue when trying to submit the form - I keep receiving a "405 POST method not allowed. No actions exist for this page" error message. My code is quite st ...

Error with TypeScript Compiler in Angular 2

Currently, I am facing an issue while trying to run tsc in my Angular 2 application directory. The error message I receive is: Error TS5023: Unknown compiler option 'moduleResolution'. This issue seems to be hindering the startup process, as ts ...

Creating TypeScript types using GraphQL code generation: Generating types without any other code

Utilizing the typescript plugin for graphql code generator As documented This TypeScript plugin is fundamental and capable of creating typings from GraphQLSchema, which can be leveraged by other typescript plugins. It creates types for all aspects of you ...

Steps for automatically closing a TextPrompt if the end user does not respond within a specific time frame

How can I programmatically close a Prompt in Microsoft Chatbot SDK v4, such as TextPrompt or ConfirmPrompt, and end the dialog after a certain period of time if the user does not reply? I attempted to use setTimeout and step.endDialog but encountered issu ...

Tips on utilizing the `arguments` property in scenarios where Parameters<...> or a similar approach is anticipated

How can you pass the arguments of a function into another function without needing to assert the parameters? Example: function f(a:number, b:number){ let args:Parameters<typeof f> = arguments // Error: Type 'IArguments' is not assignab ...

Following an update, the functioning of Visual Studio Tools for Apache Cordova ceases to operate accurately

Currently working on an ionic application using Visual Studio Tools for Apache Cordova, everything was going smoothly until I decided to update the Tools for Apache Cordova and TypeScript Tools for Visual Studio. Following the update, the Ripple emulator s ...

What is the best way to generate a JSON object with Angular and showcase its content through HTML?

Currently, I am dealing with a JSON object that is completely unfamiliar to me. Without knowing the keys or values of this object, I was able to successfully manipulate it and extract the necessary information. Ultimately, I have generated an array (whic ...

Generating Legible JavaScript Code from TypeScript

I am looking to maintain the readability of my compiled JS code, similar to how I originally wrote it, in order to make debugging easier. However, the typescript compiler introduces several changes that I would like to disable. For instance: During compi ...

Experimenting with the routerLink directive in Angular 2

Currently, I am in the process of testing routing functionality. As part of this, I have moved my navbar to a separate component called MdNavbar, which primarily consists of HTML and CSS. The RouteConfig is located in another component where MdNavbar is in ...

Different ways to determine if a given string exists within an Object

I have an object called menu which is of the type IMenu. let menu: IMenu[] = [ {restaurant : "KFC", dish:[{name: "burger", price: "1$"}, {name: "french fries", price: "2$"}, {name: "hot dog", d ...

"Create a separate function for the pipeable operator in RXJS for enhanced code

After working on some code, I came up with the following implementation this.form.valueChanges.pipe( take(1), map(val => // doSomething), exhaustMap(val => // someInner observable logic return of({someValue}) ) ).subscrib ...

How do I implement a dynamic input field in Angular 9 to retrieve data from a list or array?

I'm looking to extract all the strings from the Assignes array, which is a property of the Atm object, and populate dynamic input fields with each string. Users should be able to update or delete these strings individually. What approach can I take us ...

What is a dynamic component in Vue with Typescript?

I need help implementing type checking for my dynamic component instead of relying on 'any' as a workaround. Can someone guide me through the proper way to achieve this? <script> ... interface { [key: string]: any } const pages: page = ...

What steps should I take to export a function from a React functional component in order to create a reusable library?

Currently, I am in the midst of developing a React component library and one of my components contains a function that I want to export. The purpose of the addParticle function is to enable users of the library to dynamically insert particles into a cont ...

What distinguishes ES6 from ES2015 in the TypeScript compiler option `--libs`?

Can you explain the distinction between ES6 and ES2015 in the TypeScript compiler option here? Also, what does --libs do? https://i.sstatic.net/iUv8t.png ...

The template is displaying the string as "[object Object]"

I've implemented code in my ngOnInit function to fetch the translation for a specific text. The following function is being called: async getEmailTranslation() { const email$ = this.translate.get('SUPPORT_TRANSLATE.EMAIL'); this.emai ...

Tips for avoiding recursive error function calls in Angular 5

Is there a way to avoid repetitive function calls within a recursive function? Take a look at the following code snippet: loadFinalData(id, color){ this.data = this._test.getUrl(id, "white"); this.dataHover = this._test.getUrl(id, "blue"); } pri ...

Converting image bytes to base64 in React Native: A step-by-step guide

When requesting the product image from the backend, I want to show it to the user. The issue is: the API response contains a PNG image if the product has an image, but returns a (204 NO Content) if the product does not have an image. So, I need to display ...