Powerful data types for a method retrieving a value from an object

There is a function that retrieves a value by key from an object and provides suggestions of possible keys in the record when using it. This function also infers types from its arguments.

function get<T extends Record<string, any>, K1 extends keyof T>(key1: K1):
    (record: T | undefined) => T[K1] | undefined

const foo = { bar: 1, baz: 2 }
get('bar')(foo) // 1, get<{ a: number, b: number }>

Unfortunately, my attempts at creating a pointfree function have been unsuccessful. I have examined the implementation of the prop function in Ramda library, which works in a pointfree manner but does not provide suggestions as it allows any string as a key.

type prop = <P extends string>(key: P) => <T>(obj: Record<P, T>) => T

const foo = { bar: 1, baz: 2 }
prop('bar')(foo) // 1, prop: <"bar">(key: "bar") => <T>(obj: Record<"bar", T>) => T

EDIT:

I am aware that I cannot retrieve suggestions without specifying the record first.

prop('...') // no suggestions here
prop('...')(foo) // now I want suggestions

Answer №1

It appears highly unlikely that this task can be accomplished due to the reversed order of dependencies. If the object were provided first, this problem would not arise.

At the moment, it is necessary to manually specify the type of the object through generics, for instance:

const extract = <T extends Record<K1, any>, K1 extends keyof T>(key1: K1) =>
    (data: T) => data[key1];

const example = { value1: 10, value2: 20 }
extract<typeof example, keyof typeof example>('value1')(example)

Answer №2

Click here (test it out in typescript 3.3, no previous versions available to compare):

function get<K extends keyof any>(key: K): <T extends { [key in K]: any }>(x: T) => T[K] {
    return x => x[key]
}

const foo = { bar: 1, baz: "2" }

const x = get('bar')(foo) // number
const y = get('baz')(foo) // string

const z = get('oops')(foo) // Argument of type '{ bar: number; baz: string; }' is not assignable to parameter of type '{ oops: any; }'

Oops. Forgot to mention about automated suggestions. They may not function accurately from left to right as expected in most cases, and this example is no different.

Answer №3

If you follow the function definition approach outlined here then it becomes achievable.

For your scenario, something similar might be:

interface GetFunction<T extends { [K: string]: any }={ [K: string]: any }> {
 <K extends keyof T>(record: T, key1: K1 ): T[K1];
}
export const get : GetFunction;

To implement this, you can provide the specific key-value type if available (default generic allows skipping this step):

const foo = {bar: 1, baz: "2"}; 
const get2 = get as GetFunction<typeof foo>
const answer: 1 = get2( foo, 'bar'); //'bar'|'baz' will be suggestions

Check out a similar example in the TypeScript Playground linked below (also referenced in the question): Playground

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

Try switching out the set timeout function within typescript for Wavesurfer.js

I recently wrote a function that changes the source for an audio file using wavesurfer.js and then plays the song with the newly loaded audio file. While my code is currently functioning as intended, I can't help but feel there might be a more efficie ...

Guide to waiting for API responses with redux-saga

I have a React-Typescript app with backend calls using React Saga. I'm facing an issue where when one of my frontend functions makes a backend call, the next function starts executing before the previous one finishes. Currently, I'm using the SE ...

The addition operator cannot be used with the Number type and the value of 1

Encountering an issue where the operator '+' cannot be applied to types 'Number' and '1' buildQuerySpec() { return { PageSize: this.paging.PageCount, CurrentPage: this.paging.PageIndex + 1, MaxSize: '' ...

Retrieve the value of [routerLinkActive] in the component's class

Recently, I've been working on a tab component called TabComponent and it includes the following HTML template: <a [routerLink]='link' [routerLinkActive]="[is-active]">link label</a> <button>Close tab</button> The c ...

Updating an array by adding or removing items

I am attempting to create a method for deleting and adding items to an array, but I need easy-to-use delete and add methods since I am unfamiliar with TypeScript. export class NgForComponent implements OnInit { Numbers: number[]; constructor() { ...

Trouble encountered while using useRef in TypeScript

I'm encountering an issue with the code below; App.tsx export default function App() { const [canvasRef, canvasWidth, canvasHeight] = useCanvas(); return ( <div> <canvas ref={canvasRef} /> </div> ) ...

Issue with alert not appearing after webpage is refreshed or reloaded on Angular v10

I'm currently working on an Angular single page application. When a user clicks a button, the page refreshes and a message is supposed to be displayed. (TYPESCRIPT) import { Component, OnInit, OnDestroy } from '@angular/core'; export class ...

Importing dynamic modules within an Angular 6 library package

Currently, I am developing an Angular library that consists of numerous modules and components. One essential feature is a plugin loading system. The plugin loading mechanism functions under the assumption that the importing project (via npm) will have a ...

Angular error: Trying to access the toLowerCase property of an undefined value

I am facing an issue wherein, when I use the http.post method, I encounter an error stating "TypeError: Cannot read property 'toLowerCase' of undefined". I have attempted to downgrade Angular to version 8.0.0 in order to resolve this problem, but ...

What is the best way to manage destroyed objects?

I've been working on a PIXI.js application and I'm faced with the challenge of managing resources to prevent memory leaks. To address this issue, I am utilizing the DisplayObject.destroy method. When a display object is destroyed, many of its in ...

What is a more organized approach to creating different versions of a data type in Typescript?

In order to have different variations of content types with subtypes (such as text, photo, etc.), all sharing common properties like id, senderId, messageType, and contentData, I am considering the following approach: The messageType will remain fixed f ...

Converting types to "any" and encountering the error message "There are two distinct types with the same name, but they are not related."

I am encountering some challenges while trying to use an NPM module that I developed along with its Typescript typings in another application. To simplify the examples, I will omit properties that are not relevant to the issue at hand. Within my module&ap ...

What steps can I take to fix the 'node module error' while deploying a project on Vercel?

While working with the world-countries package, I encountered an issue during deployment in Vercel. The error message indicated that a ';' was missing in the index.d.ts file of world-countries located in the node_module directory. Here is the ex ...

Angular/Typescript code not functioning properly due to faulty expressions

What could be causing my {{ expression }} to malfunction? I have exhausted all options, yet the web browser fails to recognize this {{ expression }} or properly bind it using ng-bind. Instead, it either displays the {{ expression }} as is or not at all. C ...

Angular 2/4 - Saving User Object Information in the Front-End Instead of Repeatedly Contacting the Back-End Server

Is there a more efficient way to store and update the current user details in the frontend, without constantly making new HTTP GET requests to the backend every time a new component loads? The solution I came up with is a UserService class that handles se ...

Safeguarding user data across all components is facilitated by Angular 2

My Angular2 app uses OAuth2 with password grant type for authentication. I currently store the session token on sessionStorage, but I need to securely store additional data such as user roles. While I am aware that sessionStorage or localStorage can be ea ...

Vue cannot detect the component that is provided by my plugin

This unique plugin, currently only includes a single component (coded in TypeScript): import _Vue, { PluginObject } from "Vue"; import MyComponent from "./MyComponent.vue"; const VuePlugin: PluginObject<void> = { install(Vue: typeof _Vue): void { ...

Getting the version from package.json in Next.js can be easily achieved by accessing the `version

In my quest to retrieve the "version" from the package.json in a Next.js application, I encountered a roadblock. I attempted using process.env.npm_package_version, similar to how it is done in a Node application, but unfortunately, it returned undefined. ...

Iterating through elements within the ng-content directive in Angular using *ngFor

Is it possible to iterate through specific elements in ng-content and assign a different CSS class to each element? Currently, I am passing a parameter to enumerate child elements, but I would like to achieve this without using numbers. Here is an example ...

How can one prevent the error message saying "Type 'foo' does not exist on property" and instead return undefined?

I have a similar type defined as follows: interface Foo { bar: string baz: number } My goal is to ensure that both members are either present or neither. I attempted type X = Foo | {}, but encountered the error property 'bar' does not exist ...