From 126332096f5aa93634ff08cdea7075758eaff390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dandelion=20Man=C3=A9?= Date: Mon, 26 Aug 2019 13:20:40 +0200 Subject: [PATCH] NullUtil: add `filter` (#1324) This adds a new method called `filter` to the `NullUtil` module. `filter` enables you to filter all the null-like values out of an array in a convenient typesafe way. (It's really just a wrapper around `Array.filter((x) => x != null)` with a type signature.) Test plan: Unit tests added (for both functionality and type safety). --- src/util/null.js | 12 ++++++++++++ src/util/null.test.js | 22 ++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/util/null.js b/src/util/null.js index 54716b6..8809baf 100644 --- a/src/util/null.js +++ b/src/util/null.js @@ -79,3 +79,15 @@ export function orThrow(x: ?T, getErrorMessage: () => string): T { export function orElse(x: ?T, defaultValue: T): T { return x != null ? x : defaultValue; } + +/** + * Filter nulls and undefined out of an array, returning a new array. + * + * The functionality is easy to implement without a util method (just call + * `filter`); however Flow doesn't infer the type of the output array based on + * the callback that was passed to filter. This method basically wraps filter + * in a type-aware way. + */ +export function filter(xs: $ReadOnlyArray): T[] { + return (xs.filter((x) => x != null): any); +} diff --git a/src/util/null.test.js b/src/util/null.test.js index 333d984..eb6a18d 100644 --- a/src/util/null.test.js +++ b/src/util/null.test.js @@ -132,4 +132,26 @@ describe("util/null", () => { expect(NullUtil.orElse("", "not me")).toEqual(""); }); }); + + describe("filter", () => { + it("filters out undefined and null but not other falsey values", () => { + const x = [0, undefined, NaN, null, false, ""]; + const f = NullUtil.filter(x); + expect(f).toEqual([0, NaN, false, ""]); + }); + it("typechecks as expected", () => { + const rs: $ReadOnlyArray = ["foo", undefined]; + const _: string[] = NullUtil.filter(rs); + }); + it("returns a copy of the original array", () => { + const as = [1, 2, 3]; + const bs = NullUtil.filter(as); + expect(as).not.toBe(bs); + }); + it("doesn't allow bad coercions", () => { + const as = [1, "foo", 2]; + // $ExpectFlowError + const _: number[] = NullUtil.filter(as); + }); + }); });