From 1a5073caad74ca02adcfc9ef191c19e99c2bc06f Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Thu, 20 Oct 2022 09:33:05 +0300 Subject: [PATCH] Remove duplication from exhaustiveness checks for discriminating unions Co-authored-by: Mikko Aspiala Signed-off-by: Janne Savolainen --- .../discriminable/discriminable.ts | 10 ++++++ .../populate-application-menu.injectable.ts | 36 +++++++------------ .../preferences-navigation.tsx | 13 ++----- .../preferences/renderer/preferences.tsx | 12 ++----- 4 files changed, 27 insertions(+), 44 deletions(-) diff --git a/src/common/utils/composable-responsibilities/discriminable/discriminable.ts b/src/common/utils/composable-responsibilities/discriminable/discriminable.ts index 2152d283d5..b6b896d496 100644 --- a/src/common/utils/composable-responsibilities/discriminable/discriminable.ts +++ b/src/common/utils/composable-responsibilities/discriminable/discriminable.ts @@ -5,3 +5,13 @@ // See: https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#discriminating-unions export interface Discriminable { kind: T } + +// Note: this will fail at transpilation time, if all kinds are not instructed in switch/case. +// See: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#exhaustiveness-checking +export const checkThatAllDiscriminablesAreExhausted = (value: T) => { + const _exhaustiveCheck: never = value; + + return new Error( + `Tried to exhaust discriminables, but no instructions were found for ${(_exhaustiveCheck as any).kind}`, + ); +}; diff --git a/src/features/application-menu/main/populate-application-menu.injectable.ts b/src/features/application-menu/main/populate-application-menu.injectable.ts index a9ef99ae59..ef7732c3cd 100644 --- a/src/features/application-menu/main/populate-application-menu.injectable.ts +++ b/src/features/application-menu/main/populate-application-menu.injectable.ts @@ -10,6 +10,7 @@ import type { ApplicationMenuItemTypes } from "./menu-items/application-menu-ite import { pipeline } from "@ogre-tools/fp"; import { map, sortBy } from "lodash/fp"; import type { MenuItemRoot } from "./application-menu-item-composite.injectable"; +import { checkThatAllDiscriminablesAreExhausted } from "../../../common/utils/composable-responsibilities/discriminable/discriminable"; const populateApplicationMenuInjectable = getInjectable({ id: "populate-application-menu", @@ -32,12 +33,12 @@ export default populateApplicationMenuInjectable; const toHierarchicalElectronMenuItem = ( composite: Composite, ): MenuItemOpts => { - switch (composite.value.kind) { + const value = composite.value; + + switch (value.kind) { case "top-level-menu": { - const { - id, - value: { label, role }, - } = composite; + const { id } = composite; + const { label, role } = value; return { ...(id ? { id } : {}), @@ -53,10 +54,8 @@ const toHierarchicalElectronMenuItem = ( } case "sub-menu": { - const { - id, - value: { label }, - } = composite; + const { id } = composite; + const { label } = value; return { ...(id ? { id } : {}), @@ -71,10 +70,8 @@ const toHierarchicalElectronMenuItem = ( } case "clickable-menu-item": { - const { - id, - value: { label, onClick, keyboardShortcut }, - } = composite; + const { id } = composite; + const { label, onClick, keyboardShortcut } = value; return { ...(id ? { id } : {}), @@ -85,9 +82,7 @@ const toHierarchicalElectronMenuItem = ( } case "os-action-menu-item": { - const { - value: { label, keyboardShortcut, actionName }, - } = composite; + const { label, keyboardShortcut, actionName } = value; return { ...(label ? { label } : {}), @@ -103,14 +98,7 @@ const toHierarchicalElectronMenuItem = ( } default: { - // Note: this will fail at transpilation time, if all ApplicationMenuItemTypes - // are not handled in switch/case. - const _exhaustiveCheck: never = composite.value; - - // Note: this code is unreachable, it is here to make ts not complain about - // _exhaustiveCheck not being used. - // See: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#exhaustiveness-checking - throw new Error(`Tried to create application menu, but foreign menu item was encountered: ${_exhaustiveCheck} ${composite.value}`); + throw checkThatAllDiscriminablesAreExhausted(value); } } }; diff --git a/src/features/preferences/renderer/preference-navigation/preferences-navigation.tsx b/src/features/preferences/renderer/preference-navigation/preferences-navigation.tsx index cb2bf50af3..92448103b5 100644 --- a/src/features/preferences/renderer/preference-navigation/preferences-navigation.tsx +++ b/src/features/preferences/renderer/preference-navigation/preferences-navigation.tsx @@ -15,6 +15,7 @@ import { PreferencesNavigationTab } from "./preferences-navigation-tab"; import { compositeHasDescendant } from "../../../../common/utils/composite/composite-has-descendant/composite-has-descendant"; import type { PreferenceTabsRoot } from "../preference-items/preference-tab-root"; import { Icon } from "../../../../renderer/components/icon"; +import { checkThatAllDiscriminablesAreExhausted } from "../../../../common/utils/composable-responsibilities/discriminable/discriminable"; interface Dependencies { composite: IComputedValue>; @@ -88,16 +89,7 @@ const toNavigationHierarchy = (composite: Composite( const emptyRender = <>; + diff --git a/src/features/preferences/renderer/preferences.tsx b/src/features/preferences/renderer/preferences.tsx index 61a4933dfd..a277a90940 100644 --- a/src/features/preferences/renderer/preferences.tsx +++ b/src/features/preferences/renderer/preferences.tsx @@ -16,6 +16,7 @@ import { Map } from "../../../renderer/components/map/map"; import { observer } from "mobx-react"; import { PreferencesNavigation } from "./preference-navigation/preferences-navigation"; import Gutter from "../../../renderer/components/gutter/gutter"; +import { checkThatAllDiscriminablesAreExhausted } from "../../../common/utils/composable-responsibilities/discriminable/discriminable"; interface Dependencies { closePreferences: () => void; @@ -94,16 +95,7 @@ const toPreferenceItemHierarchy = (composite: Composite) => { } default: { - // Note: this will fail at transpilation time, if all kinds - // are not handled in switch/case. - const _exhaustiveCheck: never = value; - - // Note: this code is unreachable, it is here to make ts not complain about - // _exhaustiveCheck not being used. - // See: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#exhaustiveness-checking - throw new Error( - `Tried to create preferences, but foreign item was encountered: ${_exhaustiveCheck} ${value}`, - ); + throw checkThatAllDiscriminablesAreExhausted(value); } } };