Creating an array that contains a combination of tuples

I am currently working on converting a function to TypeScript that involves an Array of mixed type tuples, similar to Promise.all. I am struggling to set up the definitions for this particular function.

export type Type<T> = [T, boolean];

function fn<T1, T2>(list: [Type<T1>, Type<T2>]): [T1, T2] {
    return list.map(([value]) => value);
}

fn([
    ['string', true],
    [123, false],
    // More items
]);
// Output: [ 'string', 123 ]

The Promise.all method has different options such as:

all<T1, T2, T3>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): Promise<[T1, T2, T3]>;
all<T1, T2>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): Promise<[T1, T2]>;
all<T>(values: readonly (T | PromiseLike<T>)[]): Promise<T[]>;

I am wondering if there is a more efficient way in TypeScript to handle arrays without explicitly defining all possible lengths?

Answer №1

If you want the function's output to be of a specific array/tuple type T, consider defining the parameter list as a mapped array/tuple type. This means that for each numeric index I in T, the element at that index should be changed to Type<T[I]>. By using inference from mapped types, the compiler can determine the type T based on the structure of list.

One possible implementation is:

export type Type<T> = readonly [T, boolean];

function fn<T extends readonly any[]>(
  list: readonly [...{ [I in keyof T]: Type<T[I]> }]
): T {
    return list.map(([value]) => value) as any;
}

Key points to note:

  • All array types are defined as readonly since they are less restrictive than mutable arrays. Mutable arrays can be assigned to read-only arrays but not vice versa. It's unlikely that you want to disallow read-only inputs, especially considering features like const assertions.

  • The list parameter is of a variadic tuple type [...XXX], hinting to the compiler that it should interpret list as a tuple and maintain the order of elements.

  • A type assertion to any is used within the implementation of fn() due to limitations in type verification by the compiler. Extra caution must be taken to ensure type safety when using such assertions.


To test the function:

const result = fn([
    ['string', true],
    [123, false],
    [new Date(), true]
]);
// const result: [string, number, Date]

If you prefer narrower literal types, use a const assertion:

const resultNarrower = fn([
    ['string', true],
    [123, false],
    [new Date(), true]
] as const);
// const resultNarrower: ["string", 123, Date]

The readonly changes enable this more specific output type ["string", 123, Date].


Playground link to code

Answer №2

Check out the following solution for your query:


type FirstValue<T> = T extends readonly [infer First, infer Second] ? First : never;
type MappingFunction<T> = FirstValue<T>;

type MappedArray<
    ArrayType extends ReadonlyArray<unknown>,
    ResultType extends ReadonlyArray<unknown> = readonly []
    > = ArrayType extends readonly []
    ? readonly [1]
    : ArrayType extends readonly [infer X]
    ? readonly [...ResultType, MappingFunction<X>]
    : ArrayType extends readonly [infer Start, ...infer Rest]
    ? MappedArray<readonly [...Rest], readonly [...ResultType, MappingFunction<Start>]>
    : Readonly<ResultType>;

type FinalOutput = MappedArray<[['abc', true], [456, false]]>; // ['abc', 456]

export type TypeDefinition<T> = readonly [T, boolean];

function customFunction<T extends ReadonlyArray<readonly [unknown, boolean]>>(inputList: T) {
    // Type casting is required in this section
    return inputList.map(([item]) => item) as unknown as MappedArray<T>
}

const myArr = [
    ['example', true],
    [789, false],
    // Add more items here if needed
] as const;

const finalResult = customFunction(myArr); // readonly ['example', 789]

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

useEffect does not trigger a rerender on the primary parent component

I am facing an issue where the main parent component does not re-render when I change the state 'click button' in another component while using useEffect. Oddly enough, the main <App /> component only re-renders properly when I reload the p ...

Is there a distinction in Typescript between the return types of Object.seal and .freeze?

I am looking to safeguard the constant object content from any changes during runtime. This includes alterations to both the object structure and its content. The preferred method for achieving this is by using Object.freeze. interface iRO<T> { r ...

Determine the data type of a property within a JavaScript object

I am currently working with a javascript object that looks like this: venue = { id: 0, name: '', venueimage_set: [ { imageurl: '', }, ]... At a later point in my code, I need to modify the object ...

Error: The AWS amplify codegen is unable to locate any exported members within the Namespace API

Using AWS resources in my web app, such as a Cognito user pool and an AppSync GraphQL API, requires careful maintenance in a separate project. When modifications are needed, I rely on the amplify command to delete and re-import these resources: $ amplify r ...

Whenever I attempt to execute yarn build within next.js, an error always seems to occur

When attempting to compile my next.js project using the yarn build command, an error consistently occurs: Error: Export encountered errors on following paths: /settings at D:\web3\futnft\frontend\node_modules\next\ ...

Executing a method from a callback within the data() function in Vue 2.7 – Here's how!

This component uses a third-party module known as HelloWorld. This module has a prop called closeCallbacks, which is an array of callbacks that are triggered when the HelloWorld component is closed. Unfortunately, the functionality of the third-party comp ...

What steps should I take to resolve an unhandled promise error in a React TypeScript application while making an axios POST request?

I am currently working on a .tsx file to implement adding an enterprise feature. Although I can input data, clicking the submit button does not trigger any action. My application includes a page that has a button for adding a new enterprise. Upon clickin ...

Dealing with various node types in a parse tree using TypeScript: Tips and Tricks

I am in the process of converting my lexer and parser to TypeScript. You can find the current JavaScript-only code here. To simplify, I have created an example pseudocode: type X = { type: string } type A = X & { list: Array<A | B | C> } ty ...

React and TypeScript are not compatible - there is no overload that matches this call

Check out my demo This is a basic application that utilizes the GitHub api to showcase users and their avatars. Included in this app is a Users component which organizes the user data stored in the state. In order to display the data from the state, a U ...

TypeScript is failing to identify a correctly typed property

Currently, I am facing issues while converting a Material UI Dashboard from React to Typescript. The problem arises during TypeScript compilation where the property in question meets the criteria mentioned in the error message. To put it briefly, the compi ...

Fashion for the repetitive elements, activated by events

Looking for ways to style a specific table element that is generated from a repeat.for loop on a <td> tag. <td repeat.for="field of fields" class="${field.fieldKey == 'validTo' ? 'fontweigth: bold;': ''}"> b ...

Transforming time into luxon time frames and hours

Is there a way to convert this block of code from moment.js to luxon? Here is the code snippet for reference: The following code is written using moment.js, but I would like to achieve the same functionality using luxon. timezone: null, getIn: moment() ...

Error TS2322: The specified type Login cannot be assigned to the given type

I've been facing an issue while working on my app in react native. The error message I keep encountering is as follows: TS2322: Type 'typeof Login' is not assignable to type ScreenComponentType<ParamListBase, "Login"> | undefined Type ...

What is the reasoning behind TypeScript's decision to permit the omission of a function's return type?

After setting noImplicitAny to true in my tsconfig, I was surprised to find that I could still omit function return types. One instance is a getter function as shown below: get name() { return `${this.valueName} of ${this.suitName}`; } Inquiry 1: Can ...

What is the reason that setState functions properly when parsing each key separately, but fails when passed as an object?

Currently, I am delving into the world of React and TypeScript, but I have encountered a problem when trying to pass an object with a specific type in order to update the state. For some reason, the state remains unchanged. My approach involves using the ...

Updating the node startup file with Visual Studio 2015 using NodeJS/Typescript

Encountering a persistent error: Error Code: TS5055 Cannot write file C:/project/dir/server.js' because it would overwrite the input file. Project: TypeScript/JavaScript Virtual Projects Even after renaming my entry filename to nodeserver.js, the ...

The identifier "id" is not a valid index for this type

The code snippet below demonstrates the similarities and differences between the functions addThingExample2 and addThing. While addThingExample2 directly uses the union type Things, addThing utilizes a generic parameter THING extends Thing. The expression ...

Filter the angular accordion by passing a simple array value into the input

I am looking to filter my array of accordion items based on the value of the question matching the input I provide. I have tried using the filter method for this. this.accordionItems = [ { "topic":"polizze", " ...

Exporting a value from a class in Angular 2 using TypeScript

import {TranslateService, LangChangeEvent} from "@ngx-translate/core"; class CustomLanguageExporter { public currentLang : string; constructor(private translate : TranslateService) { } public static setLanguage(): string { this.tr ...

Customized interfaces utilizing generic components

Here is my simplified question. interface Identity{ name: string; } Next, we have a generic interface. interface State<T extends Identity>{ [T.name] : StateContainer<T> } Unfortunately, this approach fails and the following error is ...