Returns the element of an array that satisfies the specified predicate (the function returns a Boolean value) if and only if there is only one occurrence of the element. If the element does exist or if there is more than one occurrence of the element:
- An error will be thrown if
{ mustThrowErrorIfElementNotFoundOrMoreThan1: true }
has been specified as the 3rd parameter. In this case, TypeScript will believe that if the function has been executed successfully, the returned value has a non-null type specified (in this case, usually implicitly) via a generic parameter. - If the 3rd parameter has not been specified,
null
will be returned. To use the returned value as the value of the type specified via a generic parameter, a non-null check will be required first.
Example
const sample: Array<string> = [ "Saint Paul", "Santa Barbara", "St. Louis", "Santa Monica" ];
"Santa"
,
3 of them start with "Sa"
, but
only 1 starts with "St.".
const targetCityName: string | null = getArrayElementSatisfiesThePredicateIfSuchElementIsExactlyOne(
sample, (arrayElement: string): boolean => arrayElement.startsWith("St.")
);
The 3rd element will be returned because, according to the predicate, we only need the element that begins with «St.». In this case, there is only one such element.
However, in practice, you will not know the contents of the array in advance. So the string element which begins from "St.", must not be single, and anyway must not be even if expected, so with current combinations of the parameters of the function the null could be returned.
const targetCityName: string | null = getArrayElementSatisfiesThePredicateIfSuchElementIsExactlyOne(
sample, (arrayElement: string): boolean => arrayElement.startsWith("St.")
);
console.log(targetCityName.length);
The TypeScript error TS18047
"'targetCityName' is possibly 'null'" will occur because the length
property is actually for strings (and arrays) while the
targetCityName
variable is probably null
.
Before calling the string's properties and methods
(including length
), it is
required that you prove to
TypeScript that the value of targetCityName
is not null
.
One way to do this is to use a conditional statement like
if (targetCityName !== null) {}
.
In this case, inside the if-block TypeScript
will believe that this value is not null
.
There are other solutions, but what engineers mastering TypeScript
definitely must not to do is use
expressions like targetCityName!.length
, because it is a
serious crack in code quality.
Usage of such toxic functionality is common and mostly used with the
any
type and other indulgences,
thereby denying the meaning of using TypeScript.
console.log(
getArrayElementSatisfiesThePredicateIfSuchElementIsExactlyOne(
sample, (arrayElement: string): boolean => arrayElement.startsWith("Santa")
)
);
In this case, null
will be returned, because there are
2 elements that satisfy predicate of
not 1
.
The function name has been selected so that the behavior of the
function is intuitive.
What if you are sure that there is only 1
element that satisfies the predicate?
In this case, you can pass
{ mustThrowErrorIfElementNotFoundOrMoreThan1: true }
as the
3rd parameter.
As a result, TypeScript will not require a non-null check.
However, if contrary to expectations, if the number of elements satisfying the
predicate will not be exactly
one, UnexpectedEventError
will be thrown.
(The probability of this event is higher that than it seems.)
const matching: string = getArrayElementSatisfiesThePredicateIfSuchElementIsExactlyOne(
sample,
(arrayElement: string): boolean => arrayElement.startsWith("Santa"),
{ mustThrowErrorIfElementNotFoundOrMoreThan1: true }
);
matching
as
string
, not as
string | null
, because instead of returning
null
, the UnexpectedEventError
will the
thrown if there is no an element that satisfies the predicate or the
number of such elements is 2 or more.
Of course you can handle this exception using try/catch
,
but there is generally no advantage to doing this instead of the
above non-null check.
Comparison with Native Methods
The following native methods are neither better
nor worse than the function
getArrayElementSatisfiesThePredicateIfSuchElementIsExactlyOne
.
The one you will use depends on the required behavior when:
- There is no array element that satisfies the predicate
- There are 2 or more array elements that satisfy the predicate.
Array.prototype.find
- The common part of this native method and
getArrayElementSatisfiesThePredicateIfSuchElementIsExactlyOne
is that both find the first match with the predicate. - If there are no elements that satisfy the predicate,
Array.prototype.find
returnsundefined
whilegetArrayElementSatisfiesThePredicateIfSuchElementIsExactlyOne
either returnsnull
or throws an error depending on presence or absence of the 3rd parameter. If you have selected error throwing, TypeScript will not require a non-null check. Namely this feature could encourage programmers to use thegetArrayElementSatisfiesThePredicateIfSuchElementIsExactlyOne
function, becauseArray.prototype.find
requires non-undefined check. - If there are 2 or more
elements that satisfy the predicate,
Array.prototype.find
returns the first one and ignores the remaining ones. In contrast, oncegetArrayElementSatisfiesThePredicateIfSuchElementIsExactlyOne
finds an element that satisfies the predicate, it will also check if there are more elements that satisfy the same predicate. If this is the case, none of the elements will be returned. This feature could be useful when you need to retrieve the array element and make sure that the element is different than other ones. (For example, to guarantee that this element has a unique identifier.)
Array.prototype.filter
This method has been designed for retrieving a subarray, not for retrieving a specific element. Thus, this array method also returns an array of the elements that satisfy the predicate, not the specific element.
If there are no elements that satisfy the predicate,
an empty array will be returned.
If you try to access any element of this array (including the first
one at index 0), undefined
will be returned and you not be warned by TypeScript
(or by many other programming languages with static typing).
For example, Array<string>
and
string[]
actually refer to an
"array of an infinite number of string
elements" while, in reality,
any array is finite and sometimes empty.
Quick Input in IntelliJ IDEA Family of IDEs
Using the functionality of Live templates in the IntelliJ IDEA family if IDEs allows you to quickly input code such as a function invocation expression. To get the Live templates of the YDEE library, you need to install the official plugin of this library.
Steps for Using the Live Templates
If you have not used Live templates before, do not worry if the instructions below are too complicated. Once you have developed the habit of using Live templates (similar to the habit of using keyboard shortcuts), the following operations will take a matter of seconds.
- Copy the variable name containing the array or array expression itself to the clipboard. To make it possible for the IDE to fill in the correct value at the position of the 1st parameter, please develop the habit of copying each time before inputting the Live template of the getArrayElementSatisfiesThePredicateIfSuchElementIsExactlyOne function.
- Begin to input the function name
(getArrayElementSatisfiesThePredicateIfSuchElementIsExactlyOne).
There will be 2 autocompletes:
- Circled icon with the letter: This is the autocompletion of the function name, which is the standard functionality of the IDE. If you press the Enter key, the full function name will be inputted and the function import declaration will be inserted if required. Not bad, but better automation is possible.
- The icon with the cliche: This is the template we need.
Press Enter again.
The code template will be inserted with the value of the 1st
parameter filled in with the clipboard content, and then it will be selected by the cursor.
If you follow this manual, you will not need to edit the inserted value, so exit the
targetArray
property editing mode by pressing Enter again.
- Input the name of the parameter of the predicate that is the arrow function. This parameter is the element of the target array. However it is recommended that you use a more exact name than "element". Once you are finished inputting, press Tab. You will not need autocomplete this time, but it will interfere, press Esc first.
- Input the type of the parameter of the predicate, then
press
Enter
. - Delete the unnecessary code