A versatile Typescript array serving both as a storage for type information and input parameters

Our API is designed to handle a large amount of data, allowing users to control which properties are returned by using the fields parameter. The API definition looks like this:

interface Foo {
    A?: string;
    B?: number;
    C?: boolean;
    D?: string[];
    E?: number[];
}

Throughout our codebase, we often see patterns like this:

// simplified project-specific type definition
interface MyFoo {
    A?: string;
    C?: boolean;
}

const fields = ['A', 'C'];
...
const myFoo = await axios.get(... some URL with "fields" parameters ...) as MyFoo;

To streamline and reduce the risk of errors, I want to consolidate everything based on the main API definition. This involves creating a new type using the keys from the Foo interface and incorporating that array into the API call.

This is what I have so far:

declare function getFoo<T extends Foo, K extends keyof T>(fields: K[]): Pick<T, K>;

This approach gets me most of the way there:

const myFoo = getFoo(['A', 'C']);
type MyFoo = typeof myFoo; // type MyFoo = { A?: string; C?: boolean }

However, I am wondering if there is a more efficient way to achieve this without the need for a two-step process?

I attempted something like ReturnType<typeof getFoo> but it retrieves the whole original definition since there doesn't seem to be a way to pass the fields array into it.

Answer №1

Alright, I believe I understand the issue at hand here. Your attempt is to utilize the type of myFields in order to generate another type using Pick. The crux of the matter lies in the type FooFieldsArray. By assigning the type FooFieldsArray to myFields, specific element information within the array becomes lost.

An alternative approach would be to employ a generic function for initializing FooFieldsArray.

function initializeFields<T extends (keyof Foo)[]>(fields: [...T]){
    return fields
}

const myFields = initializeFields(["A", "C"]);
//    ^? const myFields: ["A", "C"]

The following line now functions as intended:

type newType = ConfigureMyFoo<typeof myFields>
//   ^? type newType2a = { A?: string | undefined; C?: boolean | undefined; }

Playground


If you wish to define a distinct generic type for initializeFields, there is a challenge. You will still need to deduce the type of the provided tuple, but due to TypeScript's lack of partial type inference, currying stands out as the sole solution without redundancies in the code.

function initializeFields<T>() {
    return <F extends (keyof T)[]>(fields: [...F]) => fields
}
type ExtractObject<T, K extends (keyof T)[]> = Pick<T, K[number]>;

const designatedFields = initializeFields<Foo>()(['A']);
type targetObj = ExtractObject<Foo, typeof designatedFields>;

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

Function 'await' fails to execute

Within this function, I am iterating through an array of strings, each representing a user's UID. The goal is to access each user's profile, retrieve the most recent reputation (similar to SO), and then add a map of the UID and reputation to a ne ...

Tips for modifying JSON response using a function

When I call the function buildFileTree, I store its response in a constant variable called data. const data = this.buildFileTree(dataObject, 0); The value of dataObject is: const dataObject = JSON.parse(TREE_DATA); And the content of TREE_DATA is: cons ...

Using TypeScript arrow functions to define parameters

When setting "noImplicitAny": true in TypeScript, you may encounter the following errors: Parameter 'x' implicitly has an 'any' type This error can occur with the code snippet: .do(x => console.log(x)); Another error you might s ...

The file that is currently being downloaded has the .pptx extension, but it is being

Take a look at this code snippet: const generateDownload = ({ link, data, title, settings })=> { const newLink = document.createElement('a'); const blobUrl = link || URL.createObjectURL(new Blob([data], settings)); newLink.setAt ...

When null is assigned to a type in Typescript, it does not result in an error being triggered

Could someone enlighten me on why this code is not causing an error? import { Injectable } from '@angular/core'; interface Animal{ name: string; } @Injectable() export class AnimalService { lion: Animal = null; constructor() {} get(){ ...

Animating the Click Event to Change Grid Layout in React

Can a grid layout change be animated on click in React? For instance, consider the following component: import { Box, Button, styled, useMediaQuery } from "@mui/material"; import Row1 from "./Row1"; import React from "react"; ...

Tips on how to access the names of all properties within a TypeScript class while excluding any methods

Looking to enhance the reactivity of my code, I want to render my view based on the properties of a class. How can I extract only the property names from a class and exclude methods? For instance: export class Customer { customerNumber: string; ...

I'm curious about the Next.js type that corresponds to the Redirect object

It's possible to set up redirection in Next.js by configuring it like this: module.exports = { async redirects() { return [ { source: '/about', destination: '/', permanent: true, }, ] ...

Retrieving the chosen option from a dropdown menu using AngularJS

<tr (click)="onRowClick(myDropDownList.value)"> <td> <select #myDropDownList (click)="$event.stopPropagation()" (change)="onChange($event.target.value)"> <option *ngFor="let n of numbers" [value]="n">{{n}}</option> </se ...

Can you explain the significance of this particular method signature in the TypeScript code snippet shown above?

Referencing the ngrx example, we encounter the code snippet for the method store.select, which has a complex signature with two arrows. What is the significance of this method signature? The interface definition in the type file presents the following sig ...

Using Angular 2/4/5 to Bind UTC-formatted Date to Datepicker

As someone who is just starting out with Angular and Angular Material, I have encountered an issue regarding zonedDate format for dates in my backend. The backend requires dates to be in zonedDate Format like this: 2018-04-11T02:12:04.455Z[UTC]. However, ...

What could be causing the predefined string error in useState when using TypeScript?

Is it possible to set a predefined string (enum) in React's useState hook? I attempted the following: const [color, setColor] = React.useState<"red | blue">("blue"); however, an error was thrown: Argument of type '& ...

Exploring the capabilities of Angular2 and Jasmine through testing

I have been working on a basic spec for a component and I'm curious about the test's behavior. The test is designed to verify if a component is successfully created. It seems that when the test is executed, it first compiles and runs the Compone ...

Utilizing the onscroll feature to trigger a function within a ScrollView

I am facing an issue with an animated view where I need to execute multiple events within the onScroll property. The current onScroll implementation is as follows: onScroll={ Animated.event( [{ nativeEvent: { conten ...

What is the reason a type is able to cast to an indexed collection when it is inferred, while an explicit type that seems identical is

I am puzzled by why my inferred types are considered as instances of my more general collection type while my explicit types are not. My goal was to: Have a specific part of my application work with tightly defined collections (e.g., IParents vs IBoss ...

Modify the color of the select element when it is in an open state

If you're new to Material UI, I have a select element that I would like to change the color of. When 'None' is selected, I want the background color of the input field above it to match the 'None' section. Then, when the dropdown m ...

Utilizing Typescript for Axios Response

Incorporating Typescript into my project, I encountered a tedious issue while making an API call using axios. The problem lies within handling nested data properly. Despite my belief that I have correctly typed everything, I persistently face a Typescript ...

What is the most effective method to create a versatile function in Angular that can modify the values of numerous ngModel bindings simultaneously?

After working with Angular for a few weeks, I came across a problem that has me stumped. On a page, I have a list of about 100 button inputs, each representing a different value in my database. I've linked these inputs to models as shown in this snipp ...

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

Angular 6: The Promise<any> is incompatible with the LeagueTable type

Recently delving into Angular and attempting to retrieve JSON League Table data from the API, I encountered an error message stating Type 'Promise' is not assignable to type 'LeagueTable'. leaguetable.service.ts import { Injectable } ...