Using Typescript: Generate keys in function return depending on parameter

Currently in the process of developing an SDK for a Rest API that includes an embed request parameter to fetch additional resources and add them to the response. I am exploring if there is a way, using Typescript, to extract these embed parameters while defining the return type for getImage. I have made progress with Generics, but the solution isn't as elegant as I had hoped. The following example is simplified, but I am seeking a solution that can be applied to multiple methods with varying embed options. Any advice would be greatly appreciated.

interface Type {
  ext: string
}
interface User {
  id: number
  name: string
}
interface Image {
  id: number
  name: string
}

function getImage(id: number, embed?: string | string[]) {
  // implementation of making API request goes here
}

No embeds included

getImage(1)

{
  id: 1,
  name: 'my-image'
}

Single embed provided

getImage(1, 'type')

{
  id: 1,
  name: 'my-image',
  type: {
    ext: '.png'
  }
}

Array of embeds specified

getImage(1, ['type', 'user])

{
  id: 1,
  name: 'my-image',
  type: {
    ext: '.png'
  },
  user: {
    id: 2,
    name: 'John Doe'
  }
}

Answer №1

Take a look at this next example for clarification:

interface Type {
    ext: string
}

interface User {
    id: number
    name: string
}

interface Image {
    id: number
    name: string
}


type ArrayValues<T extends ReadonlyArray<string>> = T[number]
type Ext<T extends string> = `.${T}`;

type Two<T extends string> = User & {
    [P in T]: {
        ext: Ext<'png'>
    }
}

type Three<T extends ReadonlyArray<string>> = {
    [P in ArrayValues<T>]: P extends 'type' ? {
        ext: Ext<'png'>
    } : User
}

/**
 * FUnction overloading is your best friend in such kind of situations
 */
function getImage<T extends number, U extends ReadonlyArray<string>>(id: T, embed: U): Three<U>
function getImage<T extends number, U extends string>(id: T, embed: U): Two<U>
function getImage<T extends number>(id: T): User
function getImage(id: number, embed?: string | ReadonlyArray<string>) {
    return {} as any
}


const result1 = getImage(1)
// {
//   id: 1,
//   name: 'my-image'
// }

const result2 = getImage(1, 'type')


// {
//   id: 1,
//   name: 'my-image',
//   type: {
//     ext: '.png'
//   }
// }

// Used `as const` to help TS narrow types
const result3 = getImage(1, ['type', 'user'] as const)


// {
//   id: 1,
//   name: 'my-image',
//   type: {
//     ext: '.png'
//   },
//   user: {
//     id: 2,
//     name: 'John Doe'
//   }
// }

Demo

Looking for a solution that can be applied to multiple methods with different embed options each. Any advice on how to achieve this would be greatly appreciated.

In order to make it work, you need to adjust your data structure slightly. For instance, when dealing with the array ['type','user'], treat type differently than user.

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

Executing the ngIf directive in Angular 2 when a function or click event occurs

Is there a way to display an element only when a specific function is executed or a particular click event occurs? Here's the html code snippet I'm currently working with: <sebm-google-map [latitude]="lat" [longitude]="lng" [zoom]="zoom" [map ...

Retrieve the key values from an object of a generic type

Is there a way to retrieve the keys of the object when it is of type T? I attempted to accomplish this using different methods such as: function getGenericTypeKeys<T>(): string[] { return Object.keys({} as T); } and function getGenericTypeKeys< ...

Having trouble retrieving documents from a nested collection in Firebase

I am attempting to retrieve all documents from Firebase that are based on a query. Here is my current firebase structure: https://i.stack.imgur.com/tXrX8.png Even though I have two documents inside the "ListaFavorite" collection, when I check using empty ...

Identifying data types in arrays using TypeScript type predicates

My current approach involves a function that validates if a variable is an object and not null: function isRecord(input: any): input is Record<string, any> { return input !== null && typeof input === 'object'; } This type predica ...

shared interfaces in a complete javascript application

In the past, I have typically used different languages for front-end and back-end development. But now, I want to explore the benefits of using JavaScript/TypeScript on both sides so that I can have key data models defined in one central location for both ...

Encountered error message: "Cannot assign argument of type '() => () => boolean' to parameter of type 'EffectCallback'"

I recently started working with TypeScript. I encountered an issue when attempting to utilize useEffect in TypeScript within a React context, Error: Argument of type '() => () => boolean' is not assignable to parameter of type 'Effec ...

The subscriber continues to run repeatedly, even after we have removed its subscription

The file brief-component.ts contains the following code where the drawer service is being called: this.assignDrawerService.showDrawer(parameter); In the file drawer-service.ts: private drawerSubject = new BehaviorSubject<boolean>(false); public ...

Continuously apply the template in a recursive manner in Angular 2 without reintroducing any duplicated components

Recently, I delved into the world of angular 2 and found it to be quite fascinating. However, I'm currently facing a roadblock and could really use some assistance. The scenario is as follows: I am working on creating a select box with checkboxes in ...

Encountering an issue with the message: "Property 'ref' is not available on the type 'IntrinsicAttributes'."

Having trouble implementing a link in React and TypeScript that scrolls to the correct component after clicking? I'm using the useRef Hook, but encountering an error: Type '{ ref: MutableRefObject<HTMLDivElement | null>; }' is not assi ...

JSX conditionally rendering with an inline question: <option disabled value="">Select an option</option>

Yes, I can confirm that the inline is functioning properly because in the Convert HK to Passive Segment paragraph at the top I am seeing the expected output. What I am aiming for is to display a "Choose a hotel" message when there are multiple hotels in th ...

Angular 6 component experiencing issues with animation functionality

I've implemented a Notification feature using a Notification component that displays notifications at the top of the screen. The goal is to make these notifications fade in and out smoothly. In my NotificationService, there's an array that holds ...

Angular 6: Issue TS2339 - The attribute 'value' is not recognized on the 'HTMLElement' type

I have a textarea on my website that allows users to submit comments. I want to automatically capture the date and time when the comment is submitted, and then save it in a JSON format along with the added comment: After a comment is submitted, I would li ...

Node.js does not allow the extension of the Promise object due to the absence of a base constructor with the required number of type

I'm trying to enhance the Promise object using this code snippet: class MyPromise extends Promise { constructor(executor) { super((resolve, reject) => { return executor(resolve, reject); }); } } But I keep encou ...

Tips for displaying the string value of an elementFinder when encountering an error in protractor

I have the following code snippet: export async function waitTillClickable(e: ElementFinder): Promise<ElementFinder> { const conditions = EC.visibilityOf(e); await browser.wait(conditions, DEFAULT_TIMEOUT, `Element did not return ...

What is the right way to import a module in TypeScript?

There is a module that I have declared like so: declare module conflicts { export interface Item {} export interface Item2 {} export interface Item3 {} } I attempted to import this module in a component using the following code: import * from ...

Tips for showcasing with [displayWith] in Material2's AutoComplete feature

My API returns an array and I am using Material2#AutoComplete to filter it. While it is working, I am facing an issue where I need to display a different property instead of the currently binded value in the option element. I understand that I need to uti ...

Error message "Uncaught in promise" is being triggered by the calendar function within the Ionic

Can someone assist me in creating a calendar feature for my app? My concept involves a button with text that, when clicked by the user, opens a calendar. However, I am encountering an error message: ERROR Error: Uncaught (in promise): TypeError: Cannot set ...

Setting up Storybook with Tailwindcss, ReactJS and Typescript: A comprehensive guide

What is the best way to configure Storybook to handle Tailwindcss styles and absolute paths? Just a heads up, this question and answer are self-documenting in line with this. It was quite the process to figure out, but I'm certain it will help others ...

Is there a way to invoke a function within a mat-error element?

I need to display an error message in my input field! The function will return true if both passwords match, otherwise it will return false. How can I invoke a boolean function inside mat-error! My Function: checkPasswords(): Boolean { // 'passwords& ...

Tips for restricting additional input when maximum length is reached in an Angular app

In my Angular 6 application, I am working on implementing a directive that prevents users from typing additional characters in an input field. However, I want to allow certain non-data input keys such as tab, delete, and backspace. Currently, I have an if ...