What is the best way to define a type for an array of enums in TypeScript that requires all members to be present?

Is there a way to define a type or interface that enforces the presence of certain enum values in an array? Specifically, I want to ensure that both black and white are always included. I've attempted various approaches like intersecting, omitting, and extending Array<Color> and

Array<Color.black | Color.white>
simultaneously, but haven't been successful so far:

enum Color {
  red,
  green,
  yellow,
  blue,
  black,
  white,
}

interface AnyColorsWithBlackAndWhite
  extends Array<Color | Color.black | Color.white> {}

export const Correct: AnyColorsWithBlackAndWhite = [Color.red, Color.yellow, Color.black, Color.white];

export const Wrong1: AnyColorsWithBlackAndWhite = [Color.red, Color.yellow];

export const Wrong2: AnyColorsWithBlackAndWhite = [Color.red, Color.yellow, Color.black];

Answer №1

In tackling this problem, there are two main approaches to consider: creating a function for inference and validation or forming a union of all permissible states.

enum Color {
  red,
  green,
  yellow,
  blue,
  black,
  white,
}

type RequiredElements = Color.black | Color.white

const withRequired = <
  Item extends Color,
  List extends Item[]
>(list: RequiredElements[] extends [...List] ? List : never) => list

const ok = withRequired([Color.red, Color.yellow, Color.black, Color.white]) // ok
const error = withRequired([Color.red, Color.yellow, Color.black]) // error

check out the code here

Alternatively,

enum Color {
  red,
  green,
  yellow,
  blue,
  black,
  white,
}

// Link to relevant issue for context: https://github.com/microsoft/TypeScript/issues/13298#issuecomment-692864087
type TupleUnion<U extends string | number, R extends any[] = []> = {
  [S in U]: Exclude<U, S> extends never
  ? S extends Color.black | Color.white
  ? [...R, S] : [...R, S?] : TupleUnion<Exclude<U, S>,
    S extends Color.black | Color.white ? [...R, S]
    : [...R, S?]
  >;
}[U];

type Ok = TupleUnion<Color>

export const Correct: Ok = [Color.red, Color.yellow, Color.black, Color.white]; // ok
export const COrrect1: Ok = [Color.red, Color.white, Color.yellow, Color.black]; // ok

export const Wrong1: Ok = [Color.red, Color.yellow]; // error

export const Wrong2: Ok = [Color.red, Color.green, Color.black]; // error

Access the Playground

By considering all possible states of this array, I have created an algorithm that accounts for 6! = 6 * 5 * 4 * 3 * 2 * 1 combinations.

The TupleUnion function generates a blend of all permissible states while rendering non-black and white colors optional.

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

Can you please specify the data type of the mouseMove variable?

I am struggling to define an event handler for the mouseMove event, as I am unsure of the correct type required for the handler. Upon hovering over onMouseMove, it indicates that the type should be MouseEventHandler<HTMLButtonElement>. However, atte ...

Is it possible to utilize an XML format for translation files instead of JSON in React Native?

I'm in the process of creating a react native application using the react i18next library. For translations, I've utilized XML format in android for native development. In react native, is it possible to use XML format for translation files inste ...

Disabling eslint does not prevent errors from occurring for the unicorn/filename-case rule

I have a file called payment-shipping.tsx and eslint is throwing an error Filename is not in camel case. Rename it to 'paymentShipping.tsx' unicorn/filename-case However, the file needs to be in kebab case since it's a next.js page that s ...

Tips for accessing the html id using [routerLink]

I am trying to use [routerLink] to navigate to an HTML id. blog.ts: tabsOverview: any = [ {title: 'How to create project', url: 'create-project', id: 'create-project'}, {title: 'How to invite collaborators&apos ...

Tricks to avoid using useEffect dependency in custom hook

Take a look at this snippet of code I have written: const { getConversionList, conversionList } = useConversion(); useEffect(() => { getConversionList(); }, []); I am using useConversion as a GraphQL resolver hook, however, I am encountering a Linti ...

How to determine the return type based on the quantity of arguments passed to a rest parameter function

Is there a way to create an arrow function using rest parameters that can return different types based on the number of arguments passed? For example, I am looking to implement a safeId() function with the following return type variations: safeId() // () ...

Step-by-step guide on integrating StyleX into your fresh React project

As I delve into my new project, incorporating StyleX has proven to be a bit challenging especially when working with NextJS. I find myself grappling with configuring the "next.config.js" file without causing conflicts with the existing "babel.config.js" f ...

Glitch causing incorrect images to appear while scrolling through FlashList

Currently, I am using the FlashList to showcase a list of items (avatars and titles). However, as I scroll through the list, incorrect images for the items are being displayed in a mixed-up manner. Based on the explanation provided in the documentation, t ...

Essential use of async-await with IndexedDB operations

Within my React TypeScript App, I utilize IndexedDB for data storage. To work with IndexedDB, I have a dedicated class called DB. One of the methods in this class is used to retrieve all data. public getAll(){ const promise = new Promise((resolve,reject ...

tsc converts modern ES6 code into the older ES5 version

Using TypeScript 2.2, I attempted to compile my ES6 module (js file) with the tsc compiler and it successfully converted it into valid ES5 code. Previously, I relied on tools like Google's Tracur for this task. I was unsure if this capability of compi ...

Implementing an import statement in an Electron renderer script

After following the Electron typescript quick-start code structure by cloning the repo, everything worked fine. However, when attempting to split my code into multiple .ts files and import them into the renderer script, I encountered an issue. Upon adding ...

Guide on utilizing TypeScript with dynamic object keys

I have a State object that keeps track of time in days, hours, and minutes. The state is defined as follows: type StateKeys = "days" | "hours" | "minutes"; type State = { [K in StateKeys]: number }; Now, I need to update the state based on user inpu ...

What is the best way to create a responsive Toolbar with collapsible overflowing buttons that does not rely on setTimeout()?

Currently in the process of developing a toolbar for my richTextEditor tool, Tiptap. My goal is to have buttons that exceed the width of the editor hidden under a "more" button. I stumbled upon a Reddit post discussing this concept but am encountering diff ...

Utilizing TypeScript with Express.js req.params: A Comprehensive Guide

Having an issue with my express.js controller where I am unable to use req.params The error message being displayed is 'Property 'id' is missing in type 'ParamsDictionary' but required in type 'IParam'.' I need a w ...

Guide on including source-map-loader in vue.config.js

Oh, the struggles with vue.config.js! I've been at it for hours now, trying to figure out how to add source-map-loader to vue.config.js... Here's my attempt: module.exports = { chainWebpack: (config) => { config.module ...

Simulation of documentElement language property in Jest

I am currently attempting to manipulate the documentElement lang property for my testing purposes, but I am struggling to find a solution. I have tried defining setupFiles in Jest config, which allowed me to define it but did not give me the option to chan ...

Discovering the array item by its ID using Angular 2 with Typescript

Hey everyone, I'm currently working with asp.net mvc 5 and running into an issue. When attempting to retrieve an object by its id, it keeps returning undefined. The strange thing is that the objects display fine when checking console.log(this.vtypes). ...

Please click twice in order to log in to Angular 16

Whenever I attempt to log in, I face the issue of having to click twice. The first click does not work, but the second one does. Additionally, an error message pops up: TypeError: Cannot read properties of undefined (reading 'name'). I am unsure ...

Organize the menu in Angular by sorting it alphabetically

I am currently exploring a way to organize the buttons inside the menu in alphabetical order using a function in the TypeScript file. Please see the code snippet below, which utilizes Angular Material. <mat-menu #menu3="matMenu" [overlapTrig ...

Ways to exhibit error messages on a webpage using axios in the front end,

Here is my fast API endpoint: @app.post("/api/signup", status_code=status.HTTP_200_OK) async def signup( credentials : signupcred ): try: print(credentials.email, credentials.password1, credentials.name ) response = account.create(email ...