Make sure a Typescript array contains a particular value

Currently, I am in the process of developing a dropdown-style component where the value and options are separate props. My goal is to incorporate Typescript to ensure that the value remains a valid option. Although I could perform this validation at runtime, I believe it should be achievable within TS.

Do you think this is feasible?

interface Props<N extends number> {
    count: N,
    options: Array<N | number> // Tuples aren't suitable as the length can vary
}

const Component = <N extends number>({count, options} : Props<N>) => {
  // While we could do a runtime check to validate `count` against `options`, it would be ideal to handle this earlier if possible
}

Component({count: 5, options: [1,2,3]}) // Ideally, this should return an error

Answer №1

The main concept here is to deduce the specific types of count and options and assign them to the generic types N and O. While it's straightforward for N, determining the type for O requires indicating to the compiler that we want a tuple type of number literals, not just an array of numbers like number[]. This can be achieved by enclosing O in a variadic tuple type represented as [...O].

To enforce validation, a conditional type is used to verify if N belongs to O[number]. If it doesn't belong, the conditional resolves to never, resulting in an error since nothing can be assigned to never.

interface Props<N extends number, O extends readonly number[]> {
  count: N;
  options: N extends O[number] ? readonly [...O] : never;
}
const Component = <N extends number, O extends readonly number[]>({
  count,
  options,
}: Props<N, O>) => {};

This approach works well when the array provided as options is a tuple type.

Component({ count: 5, options: [1, 2, 3] }); // error

Component({ count: 5, options: [1, 2, 3, 5] }); // ok

Component({ count: 5, options: [] as number[] }); 
// ok, all elements in options are numbers and 5 does extend number :/

If options happens to be an array type, with its elements being of type number, then the fact that 5 extends number ultimately satisfies the type checking requirements.


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

Angular 2 Quickstart encountered a 404 error when trying to retrieve the /app/main.js

I'm attempting to follow the Angular 2 quickstart guide, but I'm having trouble getting it to work. I've searched for similar questions but haven't found a solution yet. Can anyone assist me with this? Here is my code: app.component.t ...

What is the best way to assign JSON values to my class property?

I've been working on a weather application that showcases the current weather of 5 different cities. By clicking on each city, users can access a detailed view displaying the 5-day forecast for that particular location. Currently, I have defined a we ...

Is it possible to retrieve the precise key currently indexed within type declaration?

I am currently working on developing a simple type that would require a nested object key to reference the outer level. For better understanding, let's take an example: const obj = { foo: { name: 'bar', ref: 'foo' // & ...

What is the best way to apply a filter to an array of objects nested within another object in JavaScript?

I encountered an issue with one of the API responses, The response I received is as follows: [ {type: "StateCountry", state: "AL", countries: [{type: "County", countyName: "US"}, {type: "County", countyNa ...

Ways to troubleshoot a serverless framework plugin issue

I have scoured the depths of the internet trying to find an answer to my question with no luck... I am eager to tackle a serverless plugin fix, but I'm struggling with how to attach the debugging process to the code. My development environment is vs ...

What is the process for incorporating the jsnetworkx library into an ionic or angular 4 project?

When using any Ionic3 app service import * as jsnx from 'jsnetworkx'; The output error message is: Uncaught (in promise): Error: Cannot find module "lodash/lang/isPlainObject" Error: Cannot find module "lodash/lang/isPlainObject" at webpackMis ...

Utilize Hardhat and NPM to distinguish between unit tests and integration tests efficiently

Struggling with setting up two commands in my package.json for running unit tests and integration tests. I am having trouble defining the location of the testset for each type of testing. Within the scripts section of my package.json, I have created two c ...

Utilize the findIndex method to search for an element starting at a designated index

Below is a snippet of code that I have: private getNextFakeLinePosition(startPosition: number): number{ return this.models.findIndex(m => m.fakeObject); } This function is designed to return the index of the first element in the array that has ...

Using TypeScript and NestJs: Spread types can only be generated from object types

I'm encountering an issue while trying to pass two parameters using the spread operator from the book.controller to the book.service.ts service. The error message I'm receiving is: Spread types may only be created from object types It's w ...

Get the @types definition installed from a forked repository

Background Information I recently made a workaround for a single type definition in my fork of DefinitelyTyped. This fix is located on a specific branch within my fork. It's important to note that this fix is temporary and should not be merged back ...

Can you explain the significance of the | symbol in TypeScript?

My journey with TypeScript is just beginning, and I recently encountered the symbol | while working on a problem in LeetCode using Typescript. I believe it has something to do with defining variable types. Can anyone provide more insight into this? /** ...

The specified property is not found in the type 'IntrinsicAttributes & IntrinsicClassAttributes<DatePicker> & Readonly<{ children?: ReactNode; }>'

As I delve into utilizing React along with TypeScript and Material-UI components, I encounter some errors. One such error message pops up like this: The Property 'openToYearSelection' is not found on type 'IntrinsicAttributes & Intr ...

Omit certain table columns when exporting to Excel using JavaScript

I am looking to export my HTML table data into Excel sheets. After conducting a thorough research, I was able to find a solution that works for me. However, I'm facing an issue with the presence of image fields in my table data which I want to exclude ...

Best Practice for Using *ngIf in Angular (HTML / TypeScript)

In the past, I frequently used Angular's *ngIf directive in my HTML pages: <p *ngIf="var === true">Test</p> (for instance) However, there have been instances where I needed to perform multiple checks within the *ngIf directive ...

What is the best way to divide a string into an array containing both linked and non-linked elements?

I'm struggling to find the right solution to my problem. I need to create a view that is enclosed in a clickable div. The content will consist of plain text mixed with clickable URLs - the issue arises when clicking on a link also triggers the method ...

Changing the data type of a column in an Excel file from XLSX to

I am currently working with the XLSX npm package and attempting to download a sample Excel file, add some data to it, and then upload it back. The fields in the file include MOBILE NUMBER, DATE, TIME, and NAME. When I upload the file, the values for the DA ...

Problem with (click) event not triggering in innerHtml content in Angular 4

For some reason, my function isn't triggered when I click the <a... tag. Inside my component, I have the following code: public htmlstr: string; public idUser:number; this.idUser = 1; this.htmlstr = `<a (click)="delete(idUser)">${idUser}&l ...

Implementing an Asynchronous Limited Queue in JavaScript/TypeScript with async/await

Trying to grasp the concept of async/await, I am faced with the following code snippet: class AsyncQueue<T> { queue = Array<T>() maxSize = 1 async enqueue(x: T) { if (this.queue.length > this.maxSize) { // B ...

Is it possible to implement websockets with inversify-express-utils?

We are attempting to integrate websockets into our typescript application built on inversify-express-utils, but so far we have had no success: import 'reflect-metadata'; import {interfaces, InversifyExpressServer, TYPE} from 'inversify-expr ...

What is the method for extracting the types of parameters from a function type while excluding a single parameter?

Suppose I have the following interface that defines a function: export declare interface NavigationGuard { (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext): NavigationGuardReturn | Promise<NavigationGuardReturn ...