Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[fei4636.2] Add keys() method #315

Merged
merged 5 commits into from
Jul 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/jeff-wrote-this.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@khanacademy/wonder-stuff-core": minor
---

Add `keys` method as strongly-typed alternative to `Object.keys`
34 changes: 34 additions & 0 deletions packages/wonder-stuff-core/src/__tests__/keys.flowtest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// @flow
import {keys} from "../keys.js";

{
// should type returned array element as subtype of string
const obj1 = {
a: 1,
b: "2",
};

const keys1 = keys(obj1);
const _: string = keys1[0];
}

{
// should type returned array element as supertype of keys
const obj2 = {
a: 1,
b: "2",
c: [3, 4],
};

// This works because the return type is an array of a supertype of all key
// names, thanks to $Keys<>.
const keys2ok = keys(obj2);
const _: "a" | "b" | "c" = keys2ok[0];

// This errors because we try to get a key of only one type. Flow sees this
// as a bad call rather than a bad assignment. Hence the expectation on
// the callsite, not the assignment.
// $FlowExpectedError[incompatible-call]
const keys2bad = keys(obj2);
const __: "a" = keys2bad[0];
}
36 changes: 36 additions & 0 deletions packages/wonder-stuff-core/src/__tests__/keys.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// @flow
import {keys} from "../keys.js";

describe("#keys", () => {
it("should call Object.keys with the given object", () => {
// Arrange
const keysSpy = jest.spyOn(Object, "keys");
const obj = {
a: 1,
b: "2",
c: [3, 4],
};

// Act
keys(obj);

// Assert
expect(keysSpy).toHaveBeenCalledWith(obj);
});

it("should return the result of Object.keys", () => {
// Arrange
const obj = {
a: 1,
b: "2",
c: [3, 4],
};
jest.spyOn(Object, "keys").mockReturnValueOnce("THE RESULT");

// Act
const result = keys(obj);

// Assert
expect(result).toEqual("THE RESULT");
});
});
1 change: 1 addition & 0 deletions packages/wonder-stuff-core/src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @flow
export {clone} from "./clone.js";
export {keys} from "./keys.js";
export {values} from "./values.js";
export {Errors} from "./errors.js";
export {errorsFromError, Order} from "./errors-from-error.js";
Expand Down
11 changes: 11 additions & 0 deletions packages/wonder-stuff-core/src/keys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// @flow
/**
* Return an array of the enumerable keys of an object.
*
* @param {$ReadOnly<{[string]: mixed}>} obj The object for which the values are
* to be returned.
* @returns {Array<$Keys<O>>} An array of the enumerable keys of an object.
*/
export function keys<O: {[string]: mixed}>(obj: O): Array<$Keys<O>> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call forcing the type of O to be a subtype of {[string]: mixed}—otherwise keys would not behave according to its type if O were typed to have non-string keys. IMO it's pretty weird that Flow lets you have types like {[number]: string}, since that doesn't match the runtime types.

return Object.keys(obj);
}