The Excessive Getter Function Type in Mapping Types

TypeScript allows for the use of MappedTypes to generate a getter interface from a record type.

For instance, following the documentation:

type Getters<Type> = {
    [Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
};

interface Person {
    name: string;
    age: number;
    location: string;
}

type LazyPerson = Getters<Person>;

The LazyPerson is essentially equivalent to:

type LazyPerson = {
    getName: () => string;
    getAge: () => number;
    getLocation: () => string;
}

I am interested in creating something similar to Getters, but instead generating an overloaded function signature type that can differentiate the return type based on the specific key string.

function get(k: 'name'): string;
function get(k: 'age'): number;
function get(k: 'location'): string;
function get(k) {
  // perform the necessary operations
}

It seems feasible given the aforementioned type manipulation techniques.

However, I have been unable to locate any guidance on how to achieve this.

Answer №1

type Getters<Type> = {
  [Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
};

interface Person {
  name: string;
  age: number;
  location: string;
}

// credit to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
  k: infer I
) => void
  ? I
  : never;

type Values<T> = T[keyof T]

type AllowedStates<T> = {
  [Prop in keyof T]: (arg: Prop) => T[Prop]
}

type Overloading<T> = UnionToIntersection<Values<AllowedStates<T>>>

const get: Overloading<Person> = (arg: string) => {
  return null as any
}

const result = get('age') // number
const result2 = get('name') // string
const result3 = get('location') // string

Playground

You must dynamically create all allowed states - see AllowedStates

Then retrieve all Values from AllowedStates

Next, convert the union Values to intersection - UnionToIntersection. This is because an intersection of functions produces function overloading.

UPDATE

Is there a way to use that type on a class method instead of an arrow function?

Yes, you don't even need to create any overloads

interface Person {
  name: string;
  age: number;
  location: string;
}

const person = {
  name: 'string',
  age: 42,
  location: 'string',
}

class Overloads<T> {
  constructor(public obj: T) { }
  get<Key extends keyof T>(key: Key): T[Key] {
    return this.obj[key]
  }
}

const get = <T,>(obj: T) => <Key extends keyof T>(arg: Key): T[Key] => obj[arg]
{
  const getter = get(person);
  const x = getter('age') // number
  const xx = getter('name')  // string
}

const _ = new Overloads(person);
const __ = _.get('age') // number
const ___ = _.get('name') // string

Playground

Syntax for method overloading:


class Overloads {
  get(key: 'hello'): string
  get(key: 'bye'): number
  get(key: string) {
    return null as any
  }
}

UPDATE

type Person = {
  name: string;
  age: number;
  location: string;
};

class Overloads {
  get<Key extends keyof Person>(key: Key): Person[Key]
  get<Key extends keyof Person>(key: Key) {
    switch (key) {
      case 'name': {
        return 'string';
      }
      case 'age': {
        return 42
      }
      case 'location': {
        return 'string';
      }
      default: {
        return null
      }
    }

  }
}

const _ = new Overloads();
const __ = _.get('age') // number
const ___ = _.get('name') // string

UPDATE 2

type Person = {
  name: string;
  age: number;
  location: string;
  date: Date;
};

type Values<T> = T[keyof T];

type Union<T> = Values<{
  [Prop in keyof Person]: [Prop, Person[Prop]]
}>

class Overloads {
  set(...args: Union<Person>): void {
    if (args[0] === 'date') {
      console.log(args[1].getTime());
    }
  }
}

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 optimal method for assigning a value to a field in Javascript without having prior knowledge of whether it will be an input, select, textarea, radio, or checkbox field

I am currently working on a project that involves enhancing a tool which enables users to create forms and add various fields to it. Some of these fields are predefined (such as State, Province) and have specific targetable names and classes that are consi ...

The program encountered a problem stating that the 'getItem' property is not found within the 'string' type

I am utilizing Firebase to register links on a website. I'm attempting to pass the uuid in order to create a sub collection, but have been unsuccessful. Any idea what might be causing this issue? constructor(private af: AngularFirestore) {} async add ...

Assigning union values to an object array: a guide for Typescript

When working with union typed values in an object array, how should the setState() function be implemented? enum SomeStateEnum { IsRunning, Name, } type PersonState = { [SomeStateEnum.IsRunning]: boolean; [SomeStateEnum.Name]: string; }; const st ...

How to extract the chosen option from a bound list within an Angular application

Our goal is to avoid using 2-way binding in our component setup: <select type="text" formControlName="region" (change)="regionChanged($event)"> <option *ngFor="let region of regionsDDL" [ngValue]="region">{{region.name}}</option> ...

The element in TS 7023 is implicitly assigned an 'any' type due to the fact that an expression of type 'any' is not valid for indexing in type '{}'

I have implemented a select-box that includes options, labels, optgroups, and values. Is my approach correct or is there something wrong with the way I have defined my types? interface Option { label: string value: string selected?:boolean mainGrou ...

How can I send a POST request in TypeScript?

I am new to Angular 9 and facing an issue with headers in my post request. The error message says: Unnecessarily quoted property 'Authorization' found. What does this mean? What is it asking for? How can I resolve it? serviceName.service.ts: exp ...

Utilizing the 'method of' operator in Typescript

Need help with writing a function that modifies a method on a function prototype. Here is the code snippet: function inject< T, O = { [K in keyof T as T[K] extends (...args: any) => any ? K : never]: T[K]; }, K extends keyof O = keyof O, ...

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 ...

Utilizing string literals as index signatures

I've created a code snippet called MyTest that maps over an object: type MyTest<T> = { [P in keyof T]: T[P]; }; type Result = MyTest<{hello: 'world', foo: 2}>; // ^? type Result = { hello: 'world', foo: 2 } ...

What is the best way to reference typescript files without using absolute paths?

As typescript does not seem to have built-in support for absolute path references (according to this GitHub issue), it becomes difficult to organize and maintain my file references. With TypeScript files scattered across various locations in my folder stru ...

Utilizing JSDoc for establishing an index signature on a class in ES6

When working with JSDoc, achieving the equivalent of Typescript's computed property names can be a challenge. In Typescript, you'd simply define it like this: class Test { [key: string]: whatever } This syntax allows you to access these comput ...

What is the correct approach for detecting object collisions in Phaser 3?

Hey everyone, I'm facing a problem and could use some assistance. Currently, I am trying to detect when two containers collide in my project. However, the issue is that the collision is being detected before the objects even start moving on screen. It ...

Adding values to Firebase Realtime Database using Angular: A step-by-step guide

I'm looking to integrate Firebase Realtime Database with Angular in order to add values. Can anyone provide guidance on how to achieve this integration? Thanks in advance! ...

In TypeScript, Firestore withConverter may return a QueryDocumentSnapshot instead of the expected Class object

I'm currently exploring the usage of Firestore's withConverted method in Typescript to retrieve queries as instances of my customized class. Custom EventConverter Class import Event from "@/models/Event"; class EventConverter implemen ...

Update to Material-UI 4.8.1 - Is there a different method for defining the `component` property now?

Note: Please note that there was a bug in version 4.8.x which caused this issue. To resolve it, make sure to upgrade to version 4.9.0 or above. In the initial version 4.8.0, the following code would compile and run smoothly: <DialogContent> {/* us ...

The parameter 'prevState: Stock[]' does not match the expected type 'SetStateAction<Stock[] | []>'

I'm currently diving into TypeScript and I encountered the following error message that has me stumped. Interestingly, the code runs smoothly without TypeScript. Can anyone provide guidance on how to resolve this issue? Error details: Argument of typ ...

Error: TypeError encountered during UI Runtime JSON conversion of Circular Data Structure

I'm facing an issue while attempting to parse and stringify certain JSON data. The error occurs on this specific line of code: this.copyOfColumns = JSON.parse(JSON.stringify(Object.assign([], this.columns))); Below is the complete @Input (using Ang ...

React: Premature exit, Fewer hooks executed than anticipated

I'm currently working on a chrome extension project where I'm trying to update an object based on a change in the current tab. However, I keep encountering an error that says I've rendered fewer hooks than expected. How can I resolve this is ...

Creating a function generator using a string parameter in TypeScript

Is there a way to pass a string parameter to a function and retrieve an object with that key? function getFunction(name: string): { [name]: () => number } { return { [name]: () => { console.log(1); return 2; }, }; } const { m ...

Learn the method for type casting variables in Svelte 3 reactive syntax

I'm struggling with typing Svelte 3 reactive syntax variables. <script lang="ts"> import type { Player, Team } from "./types"; import { DEFAULT_PLAYER } from "./utils"; $: player = DEFAULT_PLAYER as Player; ...