Utilize TypeScript variadic syntax to capture a variable number of arguments and return an array containing elements

As TypeScript does not currently support variadic types, I am exploring alternative methods to achieve the desired functionality. I am developing a library and am willing to take extra steps to simplify the process for end users.

Below is some simplified pseudocode:

type ConstructorBase<T> = {
  new (id: number): T;
  type: string
}

type Things = { [key: string]: Thing }

private _things: Things

getThings (id: number, ...ctors: ConstructorBase<Thing>): Thing[] { 
  return ctors.map((ctor) => this._things[ctor])
}

The current approach involves users passing specially crafted constructors to retrieve instances associated with an id. While this method works well, users have to cast the obtained thing array to the expected outcome.

const [a, b] = get(15, ThingTypeA, ThingTypeB) as [ThingTypeA, ThingTypeB]
const [c, d, e] = get(15, ThingTypeC, ThingTypeD, ThingTypeE) as [ThingTypeC, ThingTypeD, ThingTypeE]

My goal is to eliminate the need for as ... in user code and rely on TypeScript to infer the types automatically.

I envision something like this:

getThings <T extends Thing[]> (id: number, ...ConstructorBase<T>): infer[] 

Experiments

I have experimented with various alternatives and explored discussions on TypeScript issues related to this topic but have yet to find a solution. My attempts at using overloads seem limited by the requirement of consistent return types, whereas I need to handle variable-length tuples. The system only recognizes the shortest implementation rather than the longest one.

getThing <T, U> (id: number, t1: ConstructorBase<T>, t2?: ConstructorBase<U>): [T, U?]
getThing <T, U, V> (id: number, t1: ConstructorBase<T>, t2?: ConstructorBase<U>, t3?: ConstructorBase<V>): [T, U?, V?] {
  // implementation
}

Considering that the scenario of exceeding six things is highly unlikely, I am open to providing overrides if necessary. However, despite my efforts, I have been unable to make it work.

Any assistance provided would be greatly appreciated!

Answer №1

If you are using overloads, it is recommended to arrange them in a specific order where the most specific overload comes first and the most general one comes last. Optional parameters should be placed towards the end as they are easier to match. However, in most cases, multiple overloads may not be necessary.

In TypeScript 3.1, you can achieve the desired behavior with a single signature by utilizing tuples in rest/spread positions for accepting input rest parameters and mapped tuples/arrays for mapping inputs to outputs.

Below is an example of a possible type signature assuming the structure of `ConstructorBase`:

type ConstructorBase<T> = new (id: number) => T;

declare function getThings<C extends ConstructorBase<any>[]>(
  id: number, ...ctors: C
): { [K in keyof C]: C[K] extends ConstructorBase<infer T> ? T : never }

Let's test it out:

declare class Thing1 { constructor(id: number); }
declare class Thing2 { constructor(id: number); }

const things = getThings(123, Thing1, Thing2);
// const things: [Thing1, Thing2]

This implementation seems to work well. Hopefully, this information proves helpful. Good luck!

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

Toggle visibility between 2 distinct Angular components

In my application, I have a Parent component that contains two different child components: inquiryForm and inquiryResponse. In certain situations, I need to toggle the visibility of these components based on specific conditions: If a user clicks the subm ...

What are the steps to integrate material-ui with styled-components effectively?

import styled from "styled-components"; import Button from "@material-ui/core/Button"; export const StyledButton = styled(Button)` margin: 20px; `; I'm having trouble adjusting the button styling. How can I add a margin to the ...

Using the index in Vue.js to locate a method within an HTML file

Currently, I am attempting to make use of the reference "index" located within <tr v-for="(note, index) in noteList" v-bind:key="index" in order to call shareToPublic(index). The method selectedID() allows for the selection of the ...

TS7053: The element is implicitly assigned an 'any' type as the expression of type 'string' cannot be used to index the type '{ username: string; email: string; '

Having trouble incorporating TypeScript into a custom React Form Component, and I keep encountering an error that I can't seem to resolve. Error message TS7053: Element implicitly has an 'any' type because expression of type 'string&apo ...

Display the next page once two distinct service requests have been received

When I need to display a page after retrieving data from two different services, service1 and service2, how can I achieve this without nesting the second service call inside the first one? Instead of chaining the service calls, I want to make separate req ...

Steps for integrating .web extension files in React Native Web using Typescript

When using react native web, there is a potential to utilize files with extensions such as .web and .android. For example: myFile.web.js myFile.android.js These files can then be included like so: import myFile from './myFile'; React Native w ...

I am experiencing a peculiar issue where a string is properly displayed in HTML, but appears as "undefined"

I'm currently facing an issue where I can successfully retrieve a string and display it on an HTML page, but I am unable to use it as a string in my TypeScript code. Below are the snippets of my code: TypeScript: user: User = new User(); construct ...

Guide to verifying the existence of a specific object mapping in JavaScript

I am currently working with an object mapping called res.payload.data[0].tnxResponse. I need to verify that the res object contains a payload property which in turn has a data property and so on. I attempted to do this using the following code, but it resu ...

The depth buffer in Webgl FrameBuffer is not being cleared properly

Currently, I am working on developing a 2D sprite renderer that utilizes render textures for custom compositing. However, I have encountered an issue where the depth buffer on the FrameBuffer is not clearing properly. Due to this, all the sprites leave a p ...

Moving the starting directory of a NodeJS application on Azure

My NodeJS app on Azure was initially written in Javascript with the app.js file located in the root directory. This file was automatically detected during deployment via Git. Recently, I converted the app to Typescript and now have a build directory, with ...

unable to reinstall due to removal of global typing

After globally installing Moment typing with the command typings install dt~moment --save --global Checking the installed typings using typings list shows: ├── <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="93fffcf7f2e0 ...

Are there more efficient alternatives to utilizing arrays and index-based functions for storing in-memory data in TypeScript?

Is there a more efficient method for storing and retrieving data besides relying on Array index-based calls? For instance: export interface EntityInterface { id: number; name: string; age: number; } export class ClassName { entities: Enti ...

How can you create a function in typescript that only allows parameters of a specific type?

Here's what I'm trying to accomplish: function validateNumber(param: ???): param is number { } If the parameter can be a number, such as number | string or number | boolean, it should be accepted by the function. However, if the type is somethin ...

Utilizing the index of the .map function in conjunction with internal methods

After running my code, I encountered the following error message: Warning: Encountered two children with the same key, `classroom-1278238`. Keys are required to be unique so that components can maintain their identity during updates. Having non-unique keys ...

An exploration of distributing union types within conditional type arrays in TypeScript

One interesting challenge I am facing involves a conditional type that utilizes a generic type T in order to determine an Array<T> type. For example: type X<T> = T extends string ? Array<T> : never; The issue arises when I input a union ...

What is the solution to fixing a 400 bad request error in Angular Routing?

Encountering an issue on a particular route where a 400 error is displayed in the screenshot every now and then. It seems to work fine for a few minutes after deleting cookies, but the error resurfaces after accessing it multiple times. Other routes are fu ...

What is the best way to invoke a function that is declared within a React component in a TypeScript class?

export class abc extends React.Component<IProps, IState> { function(name: string) { console.log("I hope to invoke this function"+ name); } render() { return ( <div>Hello</div> ); } } Next, can I cal ...

The function's return value encompasses various types, and the property does not exist within them

Currently, I am utilizing typescript for developing a NodeJS application. Within this application, I have incorporated a node module known as ts-md5, which contains a function called hashStr() that may return either a string or an Int32Array value. There ...

Is it possible to utilize an @Input() in Angular with multiple types?

Is it possible for a parent component to pass an object in @Input to the child component that may not always be the same? For instance, can I use: @Input() obj: string | number; In my scenario, I have two different objects as potential inputs: @Input() ob ...

Issue with NgOnInit not properly subscribing to observable when using mockActivatedRoute in Jasmine test scenarios

Below is a simple component that I have. All necessary imports should be assumed: //my-component.component.ts //imports, decorator, etc. routingNumber: number; ngOnInit() { this.route.params.subscribe( params => { this.routingNumber = +p ...