MapUtil: provide exact output from `toObject` (#993)

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
This commit is contained in:
William Chargin 2018-11-01 18:55:14 -07:00 committed by GitHub
parent 252d8d5c99
commit beccac822f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 20 additions and 2 deletions

View File

@ -7,8 +7,8 @@
*/
export function toObject<K: string, V, InK: K, InV: V>(
map: Map<InK, InV>
): {[K]: V} {
const result = {};
): {|[K]: V|} {
const result: {|[K]: V|} = ({}: any);
for (const [k, v] of map.entries()) {
result[k] = v;
}

View File

@ -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<number, string> = 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<string, number> = 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<string, number> = MapUtil.fromObject(o);
});
it("statically rejects a map with keys not a subtype of string", () => {
const input: {[number]: string} = {};
input[12] = "not okay";