Switch typed dispatch table to empty assertion (#133)

Summary:
This replaces the implementation of a static check from a somewhat
complicated use of higher-order types to a more simple empty-union
assertion, as suggested by jez in “Case Exhaustiveness in Flow”:
https://blog.jez.io/flow-exhaustiveness/

(I know; we’re not using Reason. One step at a time. :-) )

I adapted the implementation a bit because I prefer explicitly disabling
an ESLint warning over a no-op function call; it is not clear from the
latter that the purpose is to suppress a lint warning.

Test Plan:
In `githubPlugin.js`, add `| "ANOTHER"` to the `NodeType` type, and note
a compile-time Flow error on the appropriate line, with a very readable
error message. Note that all unit tests pass, and running the UI on
`sourcecred/sourcecred` yields correct titles for each node type present
(namely, all node types except for `ORGANIZATION` and `BOT`).

wchargin-branch: empty-union-assertion
This commit is contained in:
William Chargin 2018-04-23 13:12:44 -07:00 committed by GitHub
parent 1e311c59f4
commit e0a5118f8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -7,7 +7,6 @@ import type {Node} from "../../../../core/graph";
import type {
NodePayload,
NodeType,
NodeTypes,
IssueNodePayload,
PullRequestNodePayload,
CommentNodePayload,
@ -79,26 +78,27 @@ const adapter: PluginAdapter<NodePayload> = {
function extractAuthorTitle(node: Node<AuthorNodePayload>) {
return node.payload.login;
}
type TypedNodeToStringExtractor = <T: $Values<NodeTypes>>(
T
) => (node: Node<$ElementType<T, "payload">>) => string;
const extractors: $Exact<$ObjMap<NodeTypes, TypedNodeToStringExtractor>> = {
ISSUE: extractIssueOrPrTitle,
PULL_REQUEST: extractIssueOrPrTitle,
COMMENT: (node) => extractCommentTitle("comment", node),
PULL_REQUEST_REVIEW_COMMENT: (node) =>
extractCommentTitle("review comment", node),
PULL_REQUEST_REVIEW: extractPRReviewTitle,
USER: extractAuthorTitle,
ORGANIZATION: extractAuthorTitle,
BOT: extractAuthorTitle,
};
function fallbackAccessor(node: Node<NodePayload>) {
throw new Error(`unknown node type: ${node.address.type}`);
const anyNode: Node<any> = node;
const type: NodeType = (node.address.type: any);
switch (type) {
case "ISSUE":
case "PULL_REQUEST":
return extractIssueOrPrTitle(anyNode);
case "COMMENT":
return extractCommentTitle("comment", anyNode);
case "PULL_REQUEST_REVIEW_COMMENT":
return extractCommentTitle("review comment", anyNode);
case "PULL_REQUEST_REVIEW":
return extractPRReviewTitle(anyNode);
case "USER":
case "ORGANIZATION":
case "BOT":
return extractAuthorTitle(anyNode);
default:
// eslint-disable-next-line no-unused-expressions
(type: empty);
throw new Error(`unknown node type: ${node.address.type}`);
}
return (extractors[node.address.type] || fallbackAccessor)(
(node: Node<any>)
);
},
};