81

Is there a canonical way to find an item in an array with TypeScript?

ES6+ allows this simple/clean approach

[{"id":1}, {"id":-2}, {"id":3}].find(myObj => myObj.id < 0)  // returns {"id":-2}

TypeScript implements many ES6+ features and continues to do so. It seems likely it has at least as nice a solution, so:

How can an item be found in a array using TypeScript, considering ease of use, modern best practices, and elegance via simplicity?
(restating the question slightly to seek best approaches)

Notes

  • "item" could be a JavaScript object, or almost anything else. The example above happens to be to find plain ol' native JS objects, but many scenarios exist.

  • "canonical" is just a fancy way in Computer Science (and other fields) to say "general accepted rule or standard formula" (remember everyone here didn't know that at some point)

  • This is not about new features. Any version of JS could do this. However the form to do so gets less and less appealing the farther you go back in time.

  • TypeScript roadmap for reference.

5
  • 5
    And what kind of object? I don't see an example here. Also, TypeScript is just annotated JS. You can write vanilla JS in TypeScript and it will still run. Commented Jul 16, 2015 at 13:39
  • 1
    @Joseph the Dreamer Updated example to show searching for native JS objects. I know TS supports ES5 code, the point is that the example from ES6 is much nicer, and TS is supposedly on the path of implementing ES6 features. Commented Jul 16, 2015 at 13:51
  • @Alexander Abakumov thanks for the edit. Let's keep improving! Unless you see a disadvantage, I'm going to add to your edits, to address the word "object" within the body text. Commented Mar 11, 2019 at 20:17
  • 1
    @whitneyland: Sure! Sorry, I should have been changed to the 'item' within the body text as well, not only the title. Commented Mar 11, 2019 at 20:45
  • @Joseph the Dreamer responding to your concerns back when, I should have noted I endeavored to address all of them, and it's now all reflected in edits to the question. If I missed anything, shout out. Thx for the feedback. Commented Mar 11, 2019 at 21:17

5 Answers 5

76

Part One - Polyfill

For browsers that haven't implemented it, a polyfill for array.find. Courtesy of MDN.

if (!Array.prototype.find) {
  Array.prototype.find = function(predicate) {
    if (this == null) {
      throw new TypeError('Array.prototype.find called on null or undefined');
    }
    if (typeof predicate !== 'function') {
      throw new TypeError('predicate must be a function');
    }
    var list = Object(this);
    var length = list.length >>> 0;
    var thisArg = arguments[1];
    var value;

    for (var i = 0; i < length; i++) {
      value = list[i];
      if (predicate.call(thisArg, value, i, list)) {
        return value;
      }
    }
    return undefined;
  };
}

Part Two - Interface

You need to extend the open Array interface to include the find method.

interface Array<T> {
    find(predicate: (search: T) => boolean) : T;
}

When this arrives in TypeScript, you'll get a warning from the compiler that will remind you to delete this.

Part Three - Use it

The variable x will have the expected type... { id: number }

var x = [{ "id": 1 }, { "id": -2 }, { "id": 3 }].find(myObj => myObj.id < 0);
Sign up to request clarification or add additional context in comments.

7 Comments

Shouldn't the type definition be: find(predicate: (T) => boolean) : T;? If it is returning Array<T> then filter can be used
@Steve Fenton beautiful answer thank you. Please consider upvoting the question because I think the content is useful, but some ankle biters downvoted before I could clarify.
Use find(predicate: (value: T) => boolean): T; in the interface to get type safety in the predicate
@Sohnee used this yesterday, and found that predicate: (value: T) instead of predicate: (T) is the only way to get strong typing in the lambda predicate. Don't know why predicate: (T) doesn't. Using TS 1.7
Why isn't this supported out-of-the box? :(
|
14

For some projects it's easier to set your target to es6 in your tsconfig.json.

{
  "compilerOptions": {
    "target": "es6",
    ...

1 Comment

Note that this will break older browsers.
13

Playing with the tsconfig.json You can also targeting es5 like this :

{
    "compilerOptions": {
        "experimentalDecorators": true,
        "module": "commonjs", 
        "target": "es5"
    }
    ...

1 Comment

You did not read all of the post, there i just let what you should have focus on.
0

You could just use underscore library.

Install it:

   npm install underscore --save
   npm install @types/underscore --save-dev

Import it

   import _ = require('underscore');

Use it

    var x = _.filter(
      [{ "id": 1 }, { "id": -2 }, { "id": 3 }],
      myObj => myObj.id < 0)
    );

Comments

-1

If you need some es6 improvements not supported by Typescript, you can target es6 in your tsconfig and use Babel to convert your files in es5.

2 Comments

Downvoting because .find is an API addition and therefore adding another transpiler is uniquely unhelpful.
It works with babel-polyfill. But you are right, babel doesn't do it natively with presets. see stackoverflow.com/a/32443652/7478972

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.