Type of tuple without a specific order

Exploring Typescript typings has led me to ponder how to create a type that is a tuple with unordered element types.

For example:

type SimpleTuple = [number, string];

const tup1: SimpleTuple = [7, `7`]; // Valid
const tup2: SimpleTuple = [`7`, 7]; // 'string' is not assignable to 'number'
                                    // and vice-versa

This concept can be quite beneficial, but what if order doesn't matter or needs to be unordered?
The straightforward solution I came up with is:

type SimpleUnorderedTuple = [number, string] | [string, number];

const tup1: SimpleUnorderedTuple = [7, `7`]; // Valid
const tup2: SimpleUnorderedTuple = [`7`, 7]; // Valid

However, when dealing with numerous types, creating combinations becomes burdensome:

type ABunchOfTypes = 'these' | 'are' | 'some' | 'words' | 'just' | 'for' | 'the' | 'example';
type ComplexUnorderedTuple =
    ['these', 'are', 'some', 'words', 'just', 'for', 'the', 'example'] |
    ['these', 'are', 'some', 'words', 'just', 'for', 'example', 'the'] |
    // and so on ...

Attempting to simplify this process, I aim to achieve something like:

type ABunchOfTypes = 'these' | 'are' | 'some';
type UnorderedTuple<T> = ; //...

type ComplexUnorderedTuple = UnorderedTuple<ABunchOfTypes>;

I came across an article that mentioned:

Any subsequent value we add to the tuple variable can be any of the predefined tuple types in no particular order.

However, my attempts to replicate this have proven challenging. When defining a tuple with two elements, accessing positions beyond the tuple length is restricted.

Answer №1

If you wish to find permutations for a combination, this code will provide exactly that:

type ABunchOfTypes = 'these' | 'are' | 'some' | 'words' | 'just';

type PushFront<TailT extends any[], HeadT> =
    ((head: HeadT, ...tail: TailT) => void) extends ((...arr: infer ArrT) => void) ? ArrT : never;

type CalculatePermutations<U extends string, ResultT extends any[] = []> = {
    [k in U]: (
        [Exclude<U, k>] extends [never] ?
        PushFront<ResultT, k> :
        CalculatePermutations<Exclude<U, k>, PushFront<ResultT, k>>
    )
}[U];

var test: CalculatePermutations<ABunchOfTypes> = ['are', 'these', 'just', 'words', 'some'];

You can experiment with it on this Playground link.

Update 2

If you also want to allow empty arrays alongside non-empty ones, you can modify the code like this:

type ABunchOfTypes = 'these' | 'are' | 'some' | 'words' | 'just';

// for TypeScript 4 
type NoRepetition<U extends string, ResultT extends any[] = []> = ResultT | {
    [k in U]: NoRepetition<Exclude<U, k>, [k, ...ResultT]>
}[U];

// OK
var test: NoRepetition<ABunchOfTypes> = ['are', 'these', 'just', 'words', 'some'];
test = ['are', 'these', 'just'];
test = ['are'];
test = [];

// Not OK
test = ['are', 'these', 'are'];

View it in the Playground link.

To support union types of strings, numbers, and symbols, replace U extends string with U extends keyof any, although TypeScript's current limitation hinders going beyond this.

Answer №2

Mu-Tsun Tsai's response appears to offer a solid foundation.

type MultipleTypes = 'these' | 'are' | 'some' | 'words' | 'just';

type AddToFront<TailItems extends any[], HeadItem> =
((head: HeadItem, ...tail: TailItems) => void) extends ((...arr: infer ArrItems) => void) ? ArrItems : never;

type FindAllPermutations<Chars extends string, ResultItems extends any[] = []> = {
    [key in Chars]: (
        [Exclude<Chars, key>] extends [never] ?
        AddToFront<ResultItems, key> :
        FindAllPermutations<Exclude<Chars, key>, AddToFront<ResultItems, key>>
    ) | AddToFront<ResultItems, key>
}[Chars];

var try1: FindAllPermutations<MultipleTypes> = ['are', 'these', 'just', 'words', 'some'];
var try2: FindAllPermutations<MultipleTypes> = ['are', 'just', 'words'];
// next line results in an error
var try3: FindAllPermutations<MultipleTypes> = ['are', 'are'];

Answer №3

Below is a sample code snippet to create an enumerate function:

type ValueOf<T> = T[keyof T];

type NonEmptyArray<T> = [T, ...T[]]

type MustInclude<T, U extends T[]> =
  [T] extends [ValueOf<U>]
    ? U
    : never;

const enumerate = <T>() =>
  <U extends NonEmptyArray<T>>(...elements: MustInclude<T, U>) =>
    elements;

You can use the function as shown below:

type Word = 'these' | 'are' | 'some' | 'words';

const test1 = enumerate<Word>()('are', 'some', 'these', 'words');
const test2 = enumerate<Word>()('words', 'these', 'are', 'some');
const test3 = enumerate<Word>()('these', 'are', 'some', 'words');
  • ✅ Empty lists are not allowed
  • ✅ All values must be present
  • ✅ Duplicates are not allowed
  • ✅ Every value must be a Word

Click here for Playground link

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

Is there a way to activate decorator support while running tests using CRA 2.1?

Struggling to set up testing for a React application utilizing decorators and Typescript within Create React App v2.1.0 Aware that official support for decorators is lacking. Successfully running the application with the help of React App Rewired and @ba ...

Refreshing a page in Angular 4/5 using TypeScript

I am currently working on a single-page application that utilizes routes for navigation: this.router.navigate(['/customer-home'], { skipLocationChange: true }); While on the customer home page, I have a feature to add a new customer via a REST ...

I'm struggling with finding an answer to this question: "What is the most effective way to conduct a

I'm experimenting with a file upload. I decided to encapsulate the FileReader() inside an observable based on information I found in this discussion thread: onFileSelected(event: any) { this.importJsonFileToString(event.target.files[0]) .p ...

data not corresponding to interface

I am encountering an issue that says Type '({ name: string; href: string; icon: IconDefinition; } | { name: string; href: string; icon: IconDefinition; childs: { name: string; href: string; icon: IconDefinition; }[]; })[]' is missing the followin ...

Could I potentially pause and wait for a subscription in Angular?

I'm looking to display a list of posts similar to this: Post List In order to indicate which post is favorited by a user, I need to retrieve data from two different collections in my MongoDB database. The ngOnInit function in my post-list.component.t ...

How come the Angular8 form status registers as VALID even if the input fields are empty?

The following is the structure of the component: export class SchedulerComponent implements OnInit { schedulerForm : FormGroup; constructor(private fb: FormBuilder, private schedulerReportService: SchedulerReportService) { this. ...

What is the best way to specify the return type of a currying function?

Check out this currying function I've implemented: export interface NewIdeaCardSubmit { title: string, description: string, categories: CategoryValues } const applyInputs = (title: string) => (description: string) = ...

Using Angular NgRx - triggering an action from an effect once certain actions yield a result

I'm encountering difficulties in dispatching actions that require results from five other actions (all listed in my effect). Could someone lend a hand? Essentially, I need to trigger an action within the effect only after these five actions have retu ...

Greetings, Angular2 application with TypeScript that showcases the beauty of the world

I've been working on my first angular2 program and noticed some deviations from the expected output. typings.json: { "ambientDependencies": { "es6-shim": "github:DefinitelyTyped/DefinitelyTyped/es6-shim/es6-shim.d.ts#7de6c3dd94feaeb21f20054b9f ...

Encountered an issue while trying to assign a value to the 'value' property on an 'HTMLInputElement' within a reactive form

When I upload a picture as a data record, the image is sent to a server folder and its name is stored in the database. For this editing form, I retrieve the file name from the server and need to populate <input type="file"> in Angular 6 using reacti ...

Changes made to one order's information can impact the information of another order

Currently, I am in the process of developing a unique shopping cart feature where users input a number and a corresponding product is added to a display list. Users have the ability to adjust both the price and quantity of the products, with the total pric ...

The function navigator.canShare() encountered a permissions denial while running in Typescript

Currently, I am in the process of developing an Angular8 PWA and have successfully implemented webshare to share text content. To my excitement, Chrome has now extended its support for file sharing starting from May 2019. However, while attempting to int ...

What could be the reason for the crash caused by ngModel?

The usage of [(ngModel)] within a *ngFor-Loop is causing an endless loop and crashing the browser. This is how my HTML looks: <div class="container"> <div class="row" *ngFor="let item of controlSystemTargetViewModel.values; let index = i ...

Angular version 7.2.1 encounters an ES6 class ReferenceError when attempting to access 'X' before it has been initialized

I have encountered an issue with my TypeScript class: export class Vehicule extends TrackableEntity { vehiculeId: number; constructor() { super(); return super.proxify(this); } } The target for my TypeScript in tsconfig.json is set to es6: ...

Ways to streamline the type from typeof T down to T

One important aspect of my function is its signature, which looks like the following. waitMessage<T extends IIPCMessagesConstructors>(wantedMessageType: T): Promise<// ?? //> The definition of IIPCMessagesConstructors is crucial and consists ...

Error: Unable to locate specified column in Angular Material table

I don't understand why I am encountering this error in my code: ERROR Error: Could not find column with id "continent". I thought I had added the display column part correctly, so I'm unsure why this error is happening. <div class="exa ...

Handling onChange events for several typescript <Select> elements

As a non-TS developer, I'm delving into the realm of multiple selects and dropdown menus with Material-UI's select component. Progressing from a basic setup, I successfully implemented a single select but now face a challenge in adding another dr ...

Troubleshooting the ReferenceError: Blob is not defined problem with the heic2any library in a Next.js application

Currently, I am encountering an issue where everything is properly implemented and functioning smoothly. However, the main problem arises when I attempt to reload the page, as it results in an error message: ReferenceError: Blob is not defined. This issue ...

The element 'x' is not found within the 'unknown' type

I've been struggling with this issue. After searching through various sources like stackoverflow and github, I attempted a solution which involved adding a generic but I encountered the error message Expected 0 type arguments, but got 1. in relation t ...

React/Ionic: Avoiding SVG rendering using <img/> elements

I seem to be encountering an issue when trying to load SVG's in my React/Ionic App. I am fetching weather data from OpenWeatherMap and using the weather?.weather[0].icon property to determine which icon to display. I am utilizing icons from the follow ...