1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Remove duplication from exhaustiveness checks for discriminating unions

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
Janne Savolainen 2022-10-20 09:33:05 +03:00
parent 497ab273ae
commit 1a5073caad
No known key found for this signature in database
GPG Key ID: 8C6CFB2FFFE8F68A
4 changed files with 27 additions and 44 deletions

View File

@ -5,3 +5,13 @@
// See: https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#discriminating-unions // See: https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#discriminating-unions
export interface Discriminable<T extends string> { kind: T } export interface Discriminable<T extends string> { 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 = <T extends never>(value: T) => {
const _exhaustiveCheck: never = value;
return new Error(
`Tried to exhaust discriminables, but no instructions were found for ${(_exhaustiveCheck as any).kind}`,
);
};

View File

@ -10,6 +10,7 @@ import type { ApplicationMenuItemTypes } from "./menu-items/application-menu-ite
import { pipeline } from "@ogre-tools/fp"; import { pipeline } from "@ogre-tools/fp";
import { map, sortBy } from "lodash/fp"; import { map, sortBy } from "lodash/fp";
import type { MenuItemRoot } from "./application-menu-item-composite.injectable"; import type { MenuItemRoot } from "./application-menu-item-composite.injectable";
import { checkThatAllDiscriminablesAreExhausted } from "../../../common/utils/composable-responsibilities/discriminable/discriminable";
const populateApplicationMenuInjectable = getInjectable({ const populateApplicationMenuInjectable = getInjectable({
id: "populate-application-menu", id: "populate-application-menu",
@ -32,12 +33,12 @@ export default populateApplicationMenuInjectable;
const toHierarchicalElectronMenuItem = ( const toHierarchicalElectronMenuItem = (
composite: Composite<ApplicationMenuItemTypes>, composite: Composite<ApplicationMenuItemTypes>,
): MenuItemOpts => { ): MenuItemOpts => {
switch (composite.value.kind) { const value = composite.value;
switch (value.kind) {
case "top-level-menu": { case "top-level-menu": {
const { const { id } = composite;
id, const { label, role } = value;
value: { label, role },
} = composite;
return { return {
...(id ? { id } : {}), ...(id ? { id } : {}),
@ -53,10 +54,8 @@ const toHierarchicalElectronMenuItem = (
} }
case "sub-menu": { case "sub-menu": {
const { const { id } = composite;
id, const { label } = value;
value: { label },
} = composite;
return { return {
...(id ? { id } : {}), ...(id ? { id } : {}),
@ -71,10 +70,8 @@ const toHierarchicalElectronMenuItem = (
} }
case "clickable-menu-item": { case "clickable-menu-item": {
const { const { id } = composite;
id, const { label, onClick, keyboardShortcut } = value;
value: { label, onClick, keyboardShortcut },
} = composite;
return { return {
...(id ? { id } : {}), ...(id ? { id } : {}),
@ -85,9 +82,7 @@ const toHierarchicalElectronMenuItem = (
} }
case "os-action-menu-item": { case "os-action-menu-item": {
const { const { label, keyboardShortcut, actionName } = value;
value: { label, keyboardShortcut, actionName },
} = composite;
return { return {
...(label ? { label } : {}), ...(label ? { label } : {}),
@ -103,14 +98,7 @@ const toHierarchicalElectronMenuItem = (
} }
default: { default: {
// Note: this will fail at transpilation time, if all ApplicationMenuItemTypes throw checkThatAllDiscriminablesAreExhausted(value);
// 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}`);
} }
} }
}; };

View File

@ -15,6 +15,7 @@ import { PreferencesNavigationTab } from "./preferences-navigation-tab";
import { compositeHasDescendant } from "../../../../common/utils/composite/composite-has-descendant/composite-has-descendant"; import { compositeHasDescendant } from "../../../../common/utils/composite/composite-has-descendant/composite-has-descendant";
import type { PreferenceTabsRoot } from "../preference-items/preference-tab-root"; import type { PreferenceTabsRoot } from "../preference-items/preference-tab-root";
import { Icon } from "../../../../renderer/components/icon"; import { Icon } from "../../../../renderer/components/icon";
import { checkThatAllDiscriminablesAreExhausted } from "../../../../common/utils/composable-responsibilities/discriminable/discriminable";
interface Dependencies { interface Dependencies {
composite: IComputedValue<Composite<PreferenceTypes | PreferenceTabsRoot>>; composite: IComputedValue<Composite<PreferenceTypes | PreferenceTabsRoot>>;
@ -88,16 +89,7 @@ const toNavigationHierarchy = (composite: Composite<PreferenceTypes | Preference
} }
default: { default: {
// Note: this will fail at transpilation time, if all kinds throw checkThatAllDiscriminablesAreExhausted(value);
// 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 preference navigation, but foreign item was encountered: ${_exhaustiveCheck} ${composite.value}`,
);
} }
} }
}; };
@ -108,3 +100,4 @@ const hasContent = compositeHasDescendant<PreferenceTypes | PreferenceTabsRoot>(
const emptyRender = <></>; const emptyRender = <></>;

View File

@ -16,6 +16,7 @@ import { Map } from "../../../renderer/components/map/map";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { PreferencesNavigation } from "./preference-navigation/preferences-navigation"; import { PreferencesNavigation } from "./preference-navigation/preferences-navigation";
import Gutter from "../../../renderer/components/gutter/gutter"; import Gutter from "../../../renderer/components/gutter/gutter";
import { checkThatAllDiscriminablesAreExhausted } from "../../../common/utils/composable-responsibilities/discriminable/discriminable";
interface Dependencies { interface Dependencies {
closePreferences: () => void; closePreferences: () => void;
@ -94,16 +95,7 @@ const toPreferenceItemHierarchy = (composite: Composite<PreferenceTypes>) => {
} }
default: { default: {
// Note: this will fail at transpilation time, if all kinds throw checkThatAllDiscriminablesAreExhausted(value);
// 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}`,
);
} }
} }
}; };