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); + }); + }); });