From beccac822f411925cb80abeafce426a1dce5ed51 Mon Sep 17 00:00:00 2001 From: William Chargin Date: Thu, 1 Nov 2018 18:55:14 -0700 Subject: [PATCH] MapUtil: provide exact output from `toObject` (#993) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: The `MapUtil` map–object conversion functions used inexact objects for both input and output. They are in fact stronger than that: they can accept arbitrary inexact objects and return arbitrary exact outputs. (Recall that exact objects are subtypes of their inexact counterparts, so this is the maximally permissive combination.) Test Plan: Unit tests added. The “can return an exact object” test fails Flow before this change. The other tests would have passed already. wchargin-branch: maputil-exact-output --- src/util/map.js | 4 ++-- src/util/map.test.js | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/util/map.js b/src/util/map.js index f0865c9..23e167e 100644 --- a/src/util/map.js +++ b/src/util/map.js @@ -7,8 +7,8 @@ */ export function toObject( map: Map -): {[K]: V} { - const result = {}; +): {|[K]: V|} { + const result: {|[K]: V|} = ({}: any); for (const [k, v] of map.entries()) { result[k] = v; } diff --git a/src/util/map.test.js b/src/util/map.test.js index 7c7ae50..070e954 100644 --- a/src/util/map.test.js +++ b/src/util/map.test.js @@ -17,6 +17,14 @@ describe("util/map", () => { const output: {[Fruit]: string} = MapUtil.toObject(input); expect(output).toEqual({APPLE: "good", ORANGE: "also good"}); }); + it("can return an exact object", () => { + const _: {|[string]: number|} = MapUtil.toObject(new Map().set("a", 1)); + }); + it("can return an inexact object", () => { + // This should be free: exact objects are subtypes of their + // inexact counterparts. + const _: {[string]: number} = MapUtil.toObject(new Map().set("a", 1)); + }); it("statically rejects a map with keys not a subtype of string", () => { const input: Map = new Map() .set(12, "not okay") @@ -53,6 +61,16 @@ describe("util/map", () => { new Map().set("APPLE", "good").set("ORANGE", "also good") ); }); + it("can accept an inexact object", () => { + const o: {[string]: number} = {a: 1}; + const _: Map = MapUtil.fromObject(o); + }); + it("can accept an exact object", () => { + // This should be free: exact objects are subtypes of their + // inexact counterparts. + const o: {|[string]: number|} = ({a: 1}: any); + const _: Map = MapUtil.fromObject(o); + }); it("statically rejects a map with keys not a subtype of string", () => { const input: {[number]: string} = {}; input[12] = "not okay";