Create a function that will always output an array with the same number of elements as the input

Is there a method to generate a function that specifies "I accept an array of any type, and will return the same type with the same length"?

interface FixedLengthArray<T, L extends number> extends Array<T> {
  length: L;
}

export function shuffle<T, Arr extends Array<T>>(array: Arr):FixedLengthArray<T, Arr['length']> {
  const copy: T[] = [...array];
  for (let i = array.length - 1; i > 0; i -= 1) {
    const j = Math.floor(Math.random() * (i + 1));
    [copy[i], copy[j]] = [copy[j], copy[i]];
  }

  return copy;
}

function isPairComplete<T>(arr: T[]): arr is [T, T] {
  return arr.length === 2
}

const checkThis: (pair: [number, number]) => void = ([one, two]) => {
  //
}

checkThis(shuffle([1, 2]))

Here is a link to TypeScript playground

I am still unsure if I am using the correct approach. Perhaps reusing the same type FixedLengthArray to define my function checkThis is the right solution

Answer №1

When attempting to define fixed-length array types by assigning a numeric literal type to the length property, the compiler may not be fully equipped to handle it efficiently. It is generally recommended to utilize tuple types, as they inherently represent fixed-length array types.

If the function shuffle() takes an array/tuple type T and generates a new array with elements rearranged randomly, one way to describe the return type is as follows:

type Shuffled<T extends any[]> = { [I in keyof T]: T[number] };

Creating a mapped type over array/tuple types results in another array of the same length. In this context, each element in the resulting tuple will have a type equivalent to some element from the original tuple. The notation T[number] refers to "the type of the value stored at a number index of type T", which becomes a union of specific element types. Therefore, passing a heterogeneous tuple like [string, number] will yield a homogeneous tuple of similar length such as

[string | number, string | number]
.

Let's examine its behavior through testing:

type Test1 = Shuffled<[number, number, number]>;
// type Test1 = [number, number, number]

type Test2 = Shuffled<[string, number, string]>;
// type Test2 = [string | number, string | number, string | number]

type Test3 = Shuffled<Date[]>
// type Test3 = Date[]

The tests confirm the expected outcomes. Notably, Test3 retains its structure as Date[]; without knowledge of the input length, the output length remains unknown.


Now, we can refine the typing for your shuffle() implementation:

function shuffle<T extends any[]>(array: [...T]) {
  const copy = [...array];
  for (let i = array.length - 1; i > 0; i -= 1) {
    const j = Math.floor(Math.random() * (i + 1));
    [copy[i], copy[j]] = [copy[j], copy[i]];
  }
  return copy as Shuffled<T>;
}

const shuffled = shuffle([1, 2, 3]);
// const shuffled: [number, number, number]

const alsoShuffled = shuffle([1, "", false]);
// const alsoShuffled: [string | number | boolean, 
//   string | number | boolean, string | number | boolean]

Everything functions as intended here, though a couple of considerations emerge:

Firstly, to signify to the compiler that shuffle([1, 2, 3]) processes a tuple rather than an array, the function parameter array is assigned the variadic tuple type [...T]. Removing this would necessitate other methods to achieve the desired functionality (like the use of the checkThis() function to contextually enforce a tuple).

Secondly, the compiler lacks the sophisticated type analysis needed to validate that shuffle() actually manipulates an array of type [...T] and yields a result of type Shuffled<T>. Thus, a type assertion is employed to instruct the compiler to view copy as Shuffled<T> when returned.

Link to code on TypeScript 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

Tips for incorporating external AMD modules with Angular2 using WebPack

In my current project using Angular2, TypeScript, and WebPack, I am looking to incorporate the Esri ArcGIS JavaScript API. The API can be accessed from the following URL: By importing Map from 'esri/Map';, I aim to import the corresponding modul ...

What causes the Cassandra client driver to provide more detailed information compared to cqlsh?

I'm currently utilizing the datastax nodejs-driver to retrieve information about a keyspace from cassandra. const results = await client.execute( ` DESC KEYSPACE ${keyspace} ` ); The method client.execute provides a comprehensive object containin ...

How can a fixed type value be assigned to a portion of a type that is constrained by generics?

Experience a new aspect of Ids with my basic interface: interface Identifiable { id?: number; } Behold, a universal function that transforms record objects into entities with ids: function transformRowToObject<T extends Identifiable>(row: { id: ...

Entering information into fluctuating object fields

Suppose I have a dynamic object with a union type: data: {[key in 'num' | 'str' | 'obj']: number | string | object}; I set the object properties as follows: data.num = 1; data.str = 'text'; data.obj = {}; E ...

I am looking for a guideline that permits me to restrict the use of a form validation tool

We have developed our own version of the Validators.required form-validator that comes with Angular 7, but now we need to switch to using CustomValidators.required. To enforce this change, we are considering banning the use of the old Validators.required b ...

JavaScript Class Emit Signal for establishing a sequence of interconnected events

My Vue project includes a JavaScript class specifically for mobile devices. I'm looking to have this class emit a signal once the property 'hasEnded' is set to True for my object. How can I achieve this and chain together other events based ...

Angular ensures that the fixed display element matches the size of its neighboring sibling

I have a unique challenge where I want to fix a div to the bottom of the screen, but its width should always match the content it scrolls past. Visualize the scenario in this image: The issue arises when setting the div's width as a percentage of the ...

Tips for setting a new key and value for an existing object in TypeScript

As I transition from JavaScript to TypeScript, I am currently working on creating a Discord bot using TypeScript to familiarize myself with the environment. However, I encountered an error when attempting to add new keys to an object that was previously cr ...

Challenge with using the React useEffect hook

Incorporating the React useEffect hook into my code has been a bit challenging. Here is how I am attempting to use it: function App() { React.useEffect(() => { console.log('effect working') }, []) return ( <div className=" ...

Issue with detecting undefined in a nested function using Typescript

Examining the code snippet provided below, focus on the test getter. Why is it that const name = this.person.name does not result in an error, while const processPerson = () => this.person.name does generate an error? interface Person { name: string; ...

Using a dictionary of objects as the type for useState() in TypeScript and React functional components

I am in the process of transitioning from using classes to function components. If I already have an interface called datasets defined and want to create a state variable for it datasets: {[fieldName: string]: Dataset}; Example: {"a": dataset ...

Personalized data based on the language within Next.js

Is there a way to customize Metadata for users based on search engine keywords? To enhance SEO performance on my website, I am working on setting up unique Metadata for the two languages my website supports: English and Portuguese. Specifically, I aim to ...

Changing {number, Observable<string>} to Observable<number, string> is a necessary transformation to be made

Is there a way to convert an array of objects with the following structure: { id: number, data: Observable<string> } into an array of objects with this structure: Observable<{id: number, data: string}> using only RxJS operators? ...

Using Typescript to create a mapped type that allows for making all properties read-only, with the exception of

I encountered a problem where I didn't want to repeatedly rewrite multiple interfaces. My requirement is to have one interface with full writing capabilities, while also having a duplicate of that interface where all fields are set as read-only excep ...

Ways to display two tabs in reactjs

My typical app is running smoothly, but I am trying to implement a feature where it launches two tabs upon opening. The first tab should open with a normal path of '/' and the second one with a path of '/board'. Here's an example: ...

Exploring the power of Prosemirror with NextJS through Tiptap v2

Greetings everyone, I am a newcomer to Stack Overflow and I am reaching out for assistance regarding an issue that has arisen. The problem at hand pertains to the development of the Minimum Viable Product (MVP) for my startup which specializes in creating ...

Tips for resolving type checking errors in TypeScript and React

I encountered an error and would appreciate any assistance in resolving it. My tech stack consists of react, typescript, and Material UI. I am attempting to customize a button using the following link: https://mui.com/material-ui/customization/how-to-custo ...

Uploading images using multipart in react is causing an error and cannot be completed

Trying to upload images in the database using multipart is causing an error from the API saying 'Files can't be uploaded". Checking the API in postman shows it is working fine there. There seems to be an issue with my code, but I can't ...

What is the best way to clear all content from the "textarea" and input fields after submitting?

I'm currently using a Devextreme library for my project. I am having trouble finding a way to clear all the textarea information in the component along with other inputs when clicking on the Save button. Despite trying various methods, I have not bee ...

Unable to load content from Three.js Examples library (Error loading script for "three/examples/jsm/loaders/OBJLoader2")

In my Angular application, I have a simple setup using Three.js. When I try to import the `OBJLoader2` from `three/examples/jsm/loaders/OBJLoader2`, everything works fine except when running it with the `ts_devserver`. The browser console shows an error: G ...