An array that solely needs a single element to conform to a specific type

While I was pondering API design concepts, a thought crossed my mind. Is it feasible to define a type in this manner?

type SpecialArray<Unique, Bland> = [...Bland[], Unique, ...Bland[]];

However, the error message "A rest element cannot follow another rest element" quickly became apparent.

The closest approximation I could achieve is:

type SpecialArray<Unique, Bland> = [...Bland[], Unique] | [Unique, ...Bland[]];

This setup confines the "unique" element to either the beginning or end of the array. It's worth noting that at least one element (the unique one) must be present in the array.

I have come across Typescript: only allow one occurrence of a value in an array, but none of the proposed solutions catered for arrays of varying lengths. My search for alternative solutions yielded no results. One potential approach would involve using a helper function to enforce/infer the type, as shown below:

type Count<A extends readonly any[], T, Counter extends any[] = []> = A extends [infer H, ...infer R] ? [H] extends [T] ? Count<R, T, [...Counter, H]> : Count<R, T, Counter> : Counter;

type IsSpecial<A extends readonly any[], Unique, Bland> =
    Count<A, Unique>["length"] extends 1
        ? A[number] extends Unique | Bland
            ? A
            : never
        : never;

function helper<A extends readonly any[]>(array: IsSpecial<[...A], string, number>) { return array; }

What I'm aiming for is a type that allows me to use syntax like this instead:

const checked: SpecialArray<string, number> = [...];

Is such a concept viable? I am open to accepting a solution related to

SpecialArray<Unique, Bland>
, an alternate approach different from mine, or simply a straightforward "no" accompanied by an explanation of its impossibility.

For a partial credit score (80% 😉), consider implementing

SpecialArray<Unique, Bland, MaxLength>
, which functions up to a specified length (akin to a permutation generator).

Please bear in mind that I require some form of compile-time verification.

Answer â„–1

Creating a permutation generator for tuples of small length, as per your suggestion, is pretty simple if you utilize the "counter" technique:

type SpecialArray<U, B, N extends number, R extends any[] = [U]> =
    R["length"] extends N
        ? R
        : R | SpecialArray<U, B, N, [...R, B]> | SpecialArray<U, B, N, [B, ...R]>
;

/**************************/

type Test = SpecialArray<string, number, 5>
const success1: Test = [42, 18, 'foo', 5];
const success2: Test = ['foo', 42]

const error1: Test = [42, 18, 5];               // No `Unique` element
const error2: Test = [42, 18, 'foo', 5, 'bar']; // More than one `Unique` element
const error3: Test = [42, 18, 'foo', 5, 3, 7];  // Too many elements

TS Playground

Answer â„–2

type PreviousIndexes = [ never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
    11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...0[] ];

export type PreviousNumbers<D extends number> = PreviousIndexes[D];

type UniqueArray<T extends any[], UniqueValue, D extends number = 10> =
  [ PreviousNumbers<D> ] extends [ never ] ? never
    : T[D] extends UniqueValue ? T
        : UniqueArray<T, UniqueValue, PreviousNumbers<D>>;

usage

type WordsWithHello = UniqueArray<["hello", "world"], "hello">;
> ["hello", "world"]
type WordsWithoutWorld = UniqueArray<["hello", "other world"], "world">
> never

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

ngx-slick-carousel: a carousel that loops infinitely and adjusts responsively

I have implemented ngx-slick-carousel to showcase YouTube videos in my project. However, I am facing two main issues. Firstly, the carousel is not infinite, and when the last video is reached, there appears to be white spaces before it loops back to the fi ...

ExpressJs Request Params Override Error

I am currently utilizing express version 4.19.2 (the latest available at the time of writing) This is how I have declared the generic type Request interface ParamsDictionary { [key: string]: string; } interface Request< P = core.ParamsDictionary, ...

Error TS2339: The property 'mock' is not found on the type '(type: string) => Promise'. Unable to create a mock for SQS Queue.sendMessage()

I am attempting to simulate a call to the SQS method sendMessage() that is used in the System Under Test (SUT) like this: private async pushJobIntoQueue(network: Network) { await this.contactInteractionsQueue.sendMessage( JSON.stringify({ ...

Retrieve a mapping of keys from a generic object structure in TypeScript

I am trying to extract the key map from an object, and although I have created a function for this purpose, TypeScript is not happy with it. How can I resolve this issue without using acc: any? const origin = { a: 1, b: 'string', c: () =&g ...

Sending a POST request in Node.js and Express may result in the request body being empty or undefined

Here is a snippet of my Typescript code: import express = require('express'); const app: express.Application = express(); const port: number = 3000; app.listen(port, () => { console.log("The server is now running on port" + port); ...

Error: Unable to locate the variable 'content' in the TypeScript code

Having an issue with my navigateToApp function. In the else condition, I am calling another function called openModalDialog(content). Unfortunately, I am encountering an error stating Cannot find name content. Can someone help me identify what is wrong h ...

You cannot employ typed arguments in combination with Typescript within the VueJS framework

I'm struggling to develop a typescript vue component with some methods. Here is the script snippet. <script lang="ts"> import Vue from 'vue'; export default Vue.extend({ methods: { check(value: number) { console.log(valu ...

What is the process for setting up custom global interfaces in TypeScript using .d.ts files?

I'm currently facing an issue in my ReactJS project using Webpack2 and TypeScript. Everything is functioning perfectly except for one thing - I've been struggling to move my self-written interfaces into separate files so they are accessible throu ...

ReactJS does not support merging multiple pages into one based on user button selection

My goal is to dynamically load a component based on the user's current page. List of Pages: Executables Shop In the main screen, there is a sidebar with two icons. The primary button should set the Executables Page and the second button should set ...

What is the method for inserting a specific index into an interface array in TypeScript?

In my angular(typescript) application, I have an interface defined as follows: export interface PartnerCnic{ id: string; shipperRegCnicFront: File; shipperRegCnicBack: File; } Within my component, I have initialized an empty array for this interface li ...

Tips for dynamically incorporating filtered selections into a Mat-Select dropdown

I am seeking guidance on how to prevent changing the values of already selected values in other rows when each row of the formArray is altered. Adding controls dynamically and correctly retrieving values in filters are functioning properly. The issue arise ...

Error: Unable to initialize i18next as a function

For my current project, I am utilizing TypeScript and i18next for internalization. TypeScript version: 2.1.4 i18next version: 2.3.4 @types/i18next version: 2.3.35 In the specific file: import * as i18next from 'i18next'; i18next.init({ ...

What alternative approach can be used to substitute initEvent in typescript?

I am interested in manually triggering a MouseEvent in typescript, but I have discovered that the initEvent method is deprecated. var clickEvent =document.createEvent('MouseEvent'); clickEvent.initEvent('mouseup',true,true); Are there ...

The TypeScript namespace does not exist or cannot be located

Currently, I am working on coding in TypeScript. The specific code pertains to an Angular 2 application, but the main focus of my inquiry lies within TypeScript itself. Within my project, there are certain files that contain various models, such as the exa ...

Upon the second click, the addEventListener function is triggered

When using the window.addEventListener, I am encountering an issue where it only triggers on the second click. This is happening after I initially click on the li element to view the task information, and then click on the delete button which fires the eve ...

Automate your Excel tasks with Office Scripts: Calculate the total of values in a column depending on the criteria in another column

As a newcomer to TypeScript, I have set a goal for today - to calculate the total sum of cell values in one column of an Excel file based on values from another column. In my Excel spreadsheet, the calendar weeks are listed in column U and their correspon ...

Leverage the template pattern in React and react-hook-form to access a parent form property efficiently

In an effort to increase reusability, I developed a base generic form component that could be utilized in other child form components. The setup involves two main files: BaseForm.tsx import { useForm, FormProvider } from "react-hook-form" expor ...

Discover the subsite inventory of a SharePoint site using TypeScript

Is there a way to gather all sub-sites from my SharePoint site and organize them into a list? I initially thought of using this.context.pageContext, but could not locate it. Please excuse my seemingly simple question, as I am still learning TypeScript. ...

Nested Tagged Union Types in Typescript

Imagine having the following types (syntax similar to Elm/Haskell): type Reply = LoginReply | LogoutReply type LoginReply = LoginSucceeded | AlreadyLoggedIn String When trying to represent this in Typescript using discriminated unions, a challenge arises ...

No slides will be displayed in Ionic 2 once the workout is completed

Here is the result of the JSONOBJ: In my home.html file, I have ion-card containing a method called navigate(), which is structured as follows: navigate(event, exercise, exercise2, exercise3, exercise4){ this.navCtrl.push(exerciseSlides, { ...