Typescript includes in array

How To Check If An Array Contains A Value In Typescript

Typescript has many ways to check if an element exists in an array. Now, let’s follow three ways to check if an array contains a value in typescript.

Check if an array contains a value in Typescript

Method 1: Includes() method

In Typescript(JS), includes() method determines whether the array includes an actual value among its entries, returning true or false.

For example:

// Create an array const arrayForChecking = [2, 4, 6]; let found = arrayForChecking.includes(4); if(found === true) < console.log("has contained in array"); >else

Console Output:

Include() method works a case-sensitive search to determine and find a string that exists in an array or not, returning true or false as appropriate. In the program, “4” exists at arrayForChecking[2]. Therefore include() will return true and the “If” function will return the True condition.

Method 2: Some() Method

array.some(function(value, index, arr), this)

const letter = [«a»,»b»,»c»,»d»,»e»]; const foundLetter = letter.some(letter => letter === ‘b’); // true const foundAnotherLetter = letter.some(letter => letter === ‘h’); // false if(foundLetter === true) < console.log("The letter has been found"); >else < console.log("The letter hasn't been found"); >if(foundAnotherLetter === true) < console.log("The letter has been found"); >else

Console Output

The letter has been found // index.ts:7 The letter hasn't been found // index.ts:17

some() method checks all elements of an array if any element passes a condition and returns true if one of the arrays passes a condition and stops finding. Executes callback function once each array element. Returns false if the function returns false for all of the array elements.

Method 3: Find() Method

Similar to some() method, find() also checks all elements in the array BUT if one of the elements in array passes condition, find() method will return a Value not a boolean like some() method.

const letter = ["a","b","c","d","e"]; const found = letter.find(letter => letter === 'b'); console.log(found);

Summary

To check if an array contains a value in typescript, I showed you three ways to check it. I hope you understand and make them in your project. If you want to know more methods about an array in TS/JS, this guide can show all methods you need. Thanks for reading.

Hello, my name is Philip Cabrera. My job is a programmer, and I have 4 years of programming expertise. Follow me if you need assistance with C, C++, JS, TS, or Node.js. I hope my posts are useful to you.

Name of the university: HUMG
Major: IT
Programming Languages: C, C++, JS, TS, Node.js

Источник

TypeScript: Array.includes on narrow types

Stefan Baumgartner

The Array.prototype.includes function allows searching for a value within an array. If this value is present, the function returns true ! How handy! Of course, TypeScript has proper typings for this JavaScript functionality.

However, in certain cases, the typings can bite a little. The reason? It’s complicated! Let’s find out why.

Take this little snippet for example. We create an array called actions , which contains a set of actions in string format which we want to execute. The resulting type of this actions array is string[] .

The execute function takes any string as an argument. We check if this is a valid action, and if so, do something!

// actions: string[]
const actions = ["CREATE", "READ", "UPDATE", "DELETE"];

function execute(action: string)
if(actions.includes(action)) // 👍
// do something with action
>
>

It gets a little trickier if we want to narrow down the string[] to something more concrete, a subset of all possible strings. By adding const-context via as const , we can narrow down actions to be of type readonly [«CREATE», «READ», «UPDATE», «DELETE»] .

This is handy if we want to do exhaustiveness checking to make sure we have cases for all available actions. However, actions.includes does not agree with us:

// Adding const context
// actions: readonly ["CREATE", "READ", "UPDATE", "DELETE"]
const actions = ["CREATE", "READ", "UPDATE", "DELETE"] as const;

function execute(action: string)
if(actions.includes(action)) // 💥 ERROR
// no can do
>
>

The error reads: Argument of type ‘string’ is not assignable to parameter of type ‘“CREATE” | “READ” | “UPDATE” | “DELETE”’. – Error 2345

So why is that? Let’s look at the typings of Array and ReadonlyArray (we work with the latter one due to const-context).

interface ArrayT>  
/**
* Determines whether an array includes a certain element,
* returning true or false as appropriate.
* @param searchElement The element to search for.
* @param fromIndex The position in this array at which
* to begin searching for searchElement.
*/

includes(searchElement: T, fromIndex?: number): boolean;
>

interface ReadonlyArrayT>
/**
* Determines whether an array includes a certain element,
* returning true or false as appropriate.
* @param searchElement The element to search for.
* @param fromIndex The position in this array at which
* to begin searching for searchElement.
*/

includes(searchElement: T, fromIndex?: number): boolean;
>

The element we want to search for ( searchElement ) needs to be of the same type as the array itself! So if we have Array (or string[] or ReadonlyArray ), we can only search for strings. In our case, this would mean that action needs to be of type «CREATE» | «READ» | «UPDATE» | «DELETE» .

Suddenly, our program doesn’t make a lot of sense anymore. Why do we search for something if the type already tells us that it just can be one of four strings? If we change the type for action to «CREATE» | «READ» | «UPDATE» | «DELETE» , actions.includes becomes obsolete. If we don’t change it, TypeScript throws an error at us, and rightfully so!

One of the problems is that TypeScript lacks the possibility to check for contra-variant types with e.g. upper-bound generics. We can tell if a type should be a subset of type T with constructs like extends , we can’t check if a type is a superset of T . At least not yet!

Option 1: Re-declare ReadonlyArray #

One of the options that come into mind is changing how includes in ReadonlyArray should behave. Thanks to declaration merging, we can add our own definitions for ReadonlyArray that is a bit looser in the arguments, and more specific in the result. Like this:

interface ReadonlyArrayT>  
includes(searchElement: any, fromIndex?: number): searchElement is T;
>

This allows for a broader set of searchElement s to be passed (literally any!), and if the condition is true, we tell TypeScript through a type predicate that searchElement is T (the subset we are looking for).

Turns out, this works pretty well!

const actions = ["CREATE", "READ", "UPDATE", "DELETE"] as const;


function execute(action: string)
if(actions.includes(action))
// action: "CREATE" | "READ" | "UPDATE" | "DELETE"
>
>

Hold your horses! First of all, there’s a problem (otherwise the TypeScript team would’ve changed that already). The solution works but takes the assumption of what’s correct and what needs to be checked. If you change action to number , TypeScript would usually throw an error that you can’t search for that kind of type. actions only consists of string s, so why even look at number . This is an error you want to catch!.

// type number has no relation to actions at all
function execute(action: number)
if(actions.includes(action))
// do something
>
>

With our change to ReadonlyArray , we lose this check as searchElement is any . While the functionality of action.includes still works as intended, we might not see the right problem once we change function signatures along the way.

Also, and more importantly, we change behavior of built-in types. This might change your type-checks somewhere else, and might cause problems in the long run! If you do a “type patch” like this, be sure to do this module scoped, and not globally.

Option 2: A helper with type assertions #

As originally stated, one of the problems is that TypeScript lacks the possibility to check if a value belongs to a superset of a generic parameter. With a helper function, we can turn this relationship around!

function includesT extends U, U>(coll: ReadonlyArrayT>, el: U): el is T  
return coll.includes(el as T);
>

This includes function takes the ReadonlyArray as an argument, and searches for an element that is of type U . We check through our generic bounds that T extends U , which means that U is a superset of T (or T is a subset of U ). If the method returns true, we can say for sure that el is of the narrower type U .

The only thing that we need to make the implementation work is to do a little type assertion the moment we pass el to Array.prototype.includes . The original problem is still there! The type assertion el as T is ok though as we check possible problems already in the function signature.

This means that the moment we change e.g. action to number , we get the right booms throughout our code.

function execute(action: number)  
if(includes(actions, action)) // 💥
// Do Something
>
>

Since I haven’t got shiki-twoslash running yet (sorry, Orta), you can’t see where TypeScript throws the error. But I invite you to check it for yourself. The interesting thing is since we swapped the relationship and check if the actions array is a subset of action , TypeScript tells us we need to change the actions array.

Argument of type ‘readonly [“CREATE”, “READ”, “UPDATE”, “DELETE”]’ is not assignable to parameter of type ‘readonly number[]’. – Error 2345

But hey, I think that’s ok for the correct functionality we get! So let’s get ready to do some exhaustiveness checking:


function assertNever(input: never)
throw new Error("This is never supposed to happen!")
>

function execute(action: string)
if(includes(actions, action))
// action: "CREATE" | "READ" | "UPDATE" | "DELETE"
switch(action)
case "CREATE":
// do something
break;
case "READ":
// do something
break;
case "UPDATE":
// do something
break;
case "DELETE":
// do something
break;
default:
assertNever(action)
>
>
>

On a side note, the same solutions also work if you run into similar troubles with Array.prototype.indexOf !

Bottom line #

TypeScript aims to get all standard JavaScript functionality correct and right, but sometimes you have to make trade-offs. This case brings calls for trade-offs: Do you allow for an argument list that’s looser than you would expect, or do you throw errors for types where you already should know more?

Type assertions, declaration merging, and other tools help us to get around that in situations where the type system can’t help us. Not until it becomes better than before, by allowing us to move even further in the type space!

And, as always, here’s a playground for you to fiddle around! Here’s also a a great issue to read up on that.

Cover of Front-End Tooling

I’ve written a book on TypeScript! Check out TypeScript in 50 Lessons, published by Smashing Magazine

Источник

Читайте также:  Php запуск внешней команды
Оцените статью