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

Experimenting with NGXS selectors: A comprehensive guide

Hey there, I am currently utilizing the NGXS state management library in my application. I have a selector set up like this and everything seems to be functioning correctly. However, while testing the app, I encountered the following error message: "PrintI ...

Can I assign a value from the tagModel to ngx-chips in an Angular project?

HTML code: <tag-input class="martop20 tag-adder width100 heightauto" [onAdding]="onAdding" (onAdd)="addInternalDomain($event)" type="text" Ts code: addInternalDomain(tagTex ...

Mocked observables are returned when testing an Angular service that includes parameters

I'm currently exploring various types of unit testing and find myself struggling with a test for a service once again. Here is the function in my service that I need to test: Just to clarify: this.setParams returns an object like {name: 'Test&ap ...

Material-UI - TypeScript - Autocomplete utilizing getOptionLabel and renderOption for optimized selection functionality

I am attempting to showcase member and company using MUI Autocomplete. I have an array called suggestion that contains the options to display. [ { "__typename": "Member", "id": "ckwa91sfy0sd241b4l8rek ...

In Vue3, automatically infer a type for a slotProp based on the modelValue

In simplifying the component I aim to create, I have created the following code structure: // MyComp.vue <script setup lang="ts"> import { PropType, defineProps, defineEmits } from 'vue'; const props = defineProps({ modelVal ...

Error: Unable to generate MD5 hash for the file located at 'C:....gradle-bintray-plugin-1.7.3.jar' in Ionic framework

When attempting to use the command ionic cordova run android, an error occurred that prevented the successful execution: The process failed due to inability to create an MD5 hash for a specific file in the specified directory. This issue arose despite suc ...

employing flush for lodash's throttle wrapper

When using TypeScript with JavaScript and Angular: I am trying to use the throttle decorator from lodash to limit an API call while a user is navigating around the page, ensuring that it fires before they leave the site. In my TypeScript constructor, I h ...

The value is currently unset in the TypeScript language

The variable `this.engenes_comparte` is showing up as undefined inside the subscribe function, but it works fine outside of it. baja(){ this._restService.getEngines(this._globalService.currentFisherMan.nid).subscribe((data : any[]) => { le ...

Experiencing an error message stating 'The token ${Token[TOKEN.72]} is invalid' while using cdk synth, specifically when trying to assign the value of ec2.Vpc.cidr from cfnParameter.value

Attempting to utilize the AWS CDK CfnParameter to parameterize the CIDR value of ec2.Vpc. The aim is to make the stack reusable for VPC creation with the CIDR as a customizable value. An error stating "${Token[TOKEN.72]} is not valid" occurs during synthe ...

Compiling Vue with TypeScript: Troubleshooting common errors

Using Vue Components with Templates Multiple Times in TypeScript I am working on utilizing a component with a template multiple times within another component. The code is split between a .html file and a .ts file. The .html file structure is as follows: ...

The initial values of Typescript class members are merged directly into the class constructor

This issue has been well documented (check out Initializing variables inline during declaration vs in the constructor in Angular with TS on SO for reference), but it can lead to challenging memory problems. Take a look at the example below class Bar { ...

There seems to be an issue with Material-UI and TypeScript where a parameter of type 'string' does not have an index signature in the type 'ClassNameMap<"container" | "navBar" | "section0">'

My current project is encountering a TS Error stating "(No index signature with a parameter of type 'string' was found on type 'ClassNameMap<"container" | "navBar" | "section0">'.)" The code below is used to assign styles to vari ...

Utilizing a class structure to organize express.Router?

I've been playing around with using Express router and classes in Typescript to organize my routes. This is the approach I've taken so far. In the index.ts file, I'm trying to reference the Notes class from the notes.ts file, which has an en ...

What is the most efficient method for examining dependencies in Yarn 2 (berry)?

Is there a way to check for vulnerabilities in Yarn 2 dependencies? In Yarn 1.x, you could run yarn audit, similar to npm audit. However, this command is not available in Yarn 2. According to this issue on the Yarn berry Github, it may not be implemented ( ...

Using string replacement for effective search finding: Unleashing the power of substring matching

I have a method that adds an anchor tag for each instance of @something. The anchor tag links to a specific sub URL. Check out the code: private createAnchors(text: string) { return text.replace(/(@[^ @]+)/ig, '<a href="/home/user/$1">$1& ...

The 'toBeInTheDocument' property is not found on the 'Matchers<HTMLElement>' type

Having trouble setting up testing for a components library. Despite trying various examples and similar threads, I have not been successful. I can confirm that my setupTests.ts file is being loaded correctly (verified through a console.log). Additionally, ...

Utilizing Enum Lowercase as Index Key Type in TypeScript

Is there a way in TypeScript to use the lower case of an enum as an index key type? I have an enum defined as: export enum NameSet { Taxi = 'Taxi', Bus = 'Bus', Empty = '', } I want to define an object with keys based o ...

Angular recognizing string-array type as a string input is not correct

I am encountering a challenge with an Angular CLI component that involves working with an array of strings called "searchResult": export class ParentComponent implements OnInit { mockArray: string[] = []; searchString: string = ''; searchR ...

Is it possible to generate a Date object from a predetermined string in typescript?

I have a string with values separated by commas, and I'm trying to create a Date object from it. It seems like this is not doable -- can someone verify this and provide a solution if possible? This code snippet doesn't work : let dateString=&a ...

Interpret information in Angular 2 using Typescript

Just starting with Angular (IONIC) and need help. How can I extract the userId or id from this code? his.response = data. //Looking for guidance on accessing Json keys Response : { "userId": 1, "id": 1, "title": "sunt aut facere repellat providen ...