Skip to content

Commit

Permalink
fix: remove support for Symbols as message names, as they are not wor…
Browse files Browse the repository at this point in the history
…king with update maps

BREAKING CHANGE: Replace Symbols as message names with strings.
  • Loading branch information
atheck committed Oct 29, 2022
1 parent 330c8c7 commit fd434f0
Show file tree
Hide file tree
Showing 10 changed files with 30 additions and 33 deletions.
4 changes: 2 additions & 2 deletions src/Common.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Cmd, Dispatch } from "./Cmd";
import { Services } from "./Init";
import { MessageBase } from "./Types";
import { Message } from "./Types";

function logMessage<TMessage extends MessageBase> (name: string, msg: TMessage): void {
function logMessage<TMessage extends Message> (name: string, msg: TMessage): void {
Services.logger?.info("Elm", "message from", name, msg.name);
Services.logger?.debug("Elm", "message from", name, msg);

Expand Down
4 changes: 2 additions & 2 deletions src/ElmComponent.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from "react";
import { Cmd } from "./Cmd";
import { execCmd, logMessage, modelHasChanged } from "./Common";
import { Message, Services } from "./Init";
import { Services } from "./Init";
import { getFakeOptionsOnce } from "./Testing/fakeOptions";
import { InitFunction, Nullable, UpdateFunction } from "./Types";
import { InitFunction, Message, Nullable, UpdateFunction } from "./Types";

/**
* Abstract class for a react class component using the Elmish pattern.
Expand Down
7 changes: 2 additions & 5 deletions src/Init.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { Message } from "./Types";

interface Logger {
debug: (...args: unknown []) => void,
info: (...args: unknown []) => void,
error: (...args: unknown []) => void,
}

interface Message {
name: string | symbol,
}

type ErrorMiddlewareFunc = (error: Error) => void;
type DispatchMiddlewareFunc = (msg: Message) => void;

Expand Down Expand Up @@ -50,7 +48,6 @@ function init (options: ElmOptions): void {

export type {
Logger,
Message,
ErrorMiddlewareFunc,
DispatchMiddlewareFunc,
ElmOptions,
Expand Down
6 changes: 3 additions & 3 deletions src/Testing/createUpdateArgsFactory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { MessageBase } from "../Types";
import { Message } from "../Types";

type UpdateArgsFactory<TProps, TModel, TMessage extends MessageBase> = (msg: TMessage, modelTemplate?: Partial<TModel>, propsTemplate?: Partial<TProps>) => [TMessage, TModel, TProps];
type UpdateArgsFactory<TProps, TModel, TMessage extends Message> = (msg: TMessage, modelTemplate?: Partial<TModel>, propsTemplate?: Partial<TProps>) => [TMessage, TModel, TProps];

/**
* Creates a factory function to create a message, a model, and props which can be passed to an update function in tests.
Expand All @@ -13,7 +13,7 @@ type UpdateArgsFactory<TProps, TModel, TMessage extends MessageBase> = (msg: TMe
* // in tests
* const [msg, model, props] = createUpdateArgs(Msg.myMessage(), { ... }, , { ... });
*/
function createUpdateArgsFactory<TProps, TModel, TMessage extends MessageBase> (initModel: () => TModel, initProps: () => TProps): UpdateArgsFactory<TProps, TModel, TMessage> {
function createUpdateArgsFactory<TProps, TModel, TMessage extends Message> (initModel: () => TModel, initProps: () => TProps): UpdateArgsFactory<TProps, TModel, TMessage> {
return function (msg: TMessage, modelTemplate?: Partial<TModel>, propsTemplate?: Partial<TProps>): [TMessage, TModel, TProps] {
return [
msg,
Expand Down
10 changes: 5 additions & 5 deletions src/Testing/fakeOptions.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Dispatch } from "../Cmd";
import { MessageBase, Nullable } from "../Types";
import { Message, Nullable } from "../Types";

/**
* Options for the `renderWithModel` function.
* @interface RenderWithModelOptions
* @template TModel The type of the model.
* @template TMessage The type of the messages discriminated union.
*/
interface RenderWithModelOptions<TModel, TMessage extends MessageBase> {
interface RenderWithModelOptions<TModel, TMessage extends Message> {
/**
* The model to use when rendering the component.
* @type {TModel}
Expand All @@ -21,13 +21,13 @@ interface RenderWithModelOptions<TModel, TMessage extends MessageBase> {
dispatch?: Dispatch<TMessage>,
}

let currentFakeOptions: Nullable<RenderWithModelOptions<unknown, MessageBase>>;
let currentFakeOptions: Nullable<RenderWithModelOptions<unknown, Message>>;

function setFakeOptions (options: Nullable<RenderWithModelOptions<unknown, MessageBase>>): void {
function setFakeOptions (options: Nullable<RenderWithModelOptions<unknown, Message>>): void {
currentFakeOptions = options;
}

function getFakeOptionsOnce<TModel, TMessage extends MessageBase> (): Nullable<RenderWithModelOptions<TModel, TMessage>> {
function getFakeOptionsOnce<TModel, TMessage extends Message> (): Nullable<RenderWithModelOptions<TModel, TMessage>> {
const temp = currentFakeOptions;

currentFakeOptions = null;
Expand Down
4 changes: 2 additions & 2 deletions src/Testing/getUpdateFn.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { MessageBase, UpdateMap, UpdateReturnType } from "../Types";
import { Message, UpdateMap, UpdateReturnType } from "../Types";
import { callUpdateMap } from "../useElmish";

/**
* Creates an update function out of an UpdateMap.
* @param {UpdateMap<TProps, TModel, TMessage>} updateMap The UpdateMap.
* @returns {(msg: TMessage, model: TModel, props: TProps) => UpdateReturnType<TModel, TMessage>} The created update function which can be used in tests.
*/
function getUpdateFn<TProps, TModel, TMessage extends MessageBase> (updateMap: UpdateMap<TProps, TModel, TMessage>): (msg: TMessage, model: TModel, props: TProps) => UpdateReturnType<TModel, TMessage> {
function getUpdateFn<TProps, TModel, TMessage extends Message> (updateMap: UpdateMap<TProps, TModel, TMessage>): (msg: TMessage, model: TModel, props: TProps) => UpdateReturnType<TModel, TMessage> {
return function (msg: TMessage, model: TModel, props: TProps): UpdateReturnType<TModel, TMessage> {
return callUpdateMap(updateMap, msg, model, props);
};
Expand Down
6 changes: 3 additions & 3 deletions src/Testing/renderWithModel.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MessageBase } from "../Types";
import { Message } from "../Types";
import { RenderWithModelOptions, setFakeOptions } from "./fakeOptions";

/**
Expand All @@ -10,9 +10,9 @@ import { RenderWithModelOptions, setFakeOptions } from "./fakeOptions";
* @param {(TModel | RenderWithModelOptions<TModel, TMessage>)} options The model or an options object.
* @returns {TResult} The returned value of the `render` function.
*/
function renderWithModel<TModel, TMessage extends MessageBase, TResult> (render: () => TResult, options: TModel | RenderWithModelOptions<TModel, TMessage>): TResult {
function renderWithModel<TModel, TMessage extends Message, TResult> (render: () => TResult, options: TModel | RenderWithModelOptions<TModel, TMessage>): TResult {
if ("model" in options && "dispatch" in options) {
setFakeOptions(options as RenderWithModelOptions<unknown, MessageBase>);
setFakeOptions(options as RenderWithModelOptions<unknown, Message>);
} else {
setFakeOptions({
model: options,
Expand Down
8 changes: 4 additions & 4 deletions src/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Cmd } from "./Cmd";

type Nullable<T> = T | null;

interface MessageBase {
name: string | symbol,
interface Message {
name: string,
}

/**
Expand Down Expand Up @@ -31,13 +31,13 @@ type UpdateFunction<TProps, TModel, TMessage> = (model: TModel, msg: TMessage, p
* Type for mapping messages to functions.
* Use this type to create your update logic for the useElmish hook.
*/
type UpdateMap<TProps, TModel, TMessage extends MessageBase> = {
type UpdateMap<TProps, TModel, TMessage extends Message> = {
[M in TMessage as M["name"]]: (msg: M, model: TModel, props: TProps) => UpdateReturnType<TModel, TMessage>;
};

export type {
Nullable,
MessageBase,
Message,
MsgSource,
InitResult,
InitFunction,
Expand Down
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Cmd, createCmd, Dispatch } from "./Cmd";
import { ElmComponent } from "./ElmComponent";
import { errorHandler, ErrorMessage, errorMsg, handleError } from "./ErrorHandling";
import { init, Logger, Message } from "./Init";
import { InitResult, MsgSource, UpdateMap, UpdateReturnType } from "./Types";
import { init, Logger } from "./Init";
import { InitResult, Message, MsgSource, UpdateMap, UpdateReturnType } from "./Types";
import { SubscriptionResult, useElmish } from "./useElmish";

export type {
Expand Down
10 changes: 5 additions & 5 deletions src/useElmish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Cmd, Dispatch } from "./Cmd";
import { execCmd, logMessage, modelHasChanged } from "./Common";
import { Services } from "./Init";
import { getFakeOptionsOnce } from "./Testing/fakeOptions";
import { InitFunction, MessageBase, Nullable, UpdateFunction, UpdateMap, UpdateReturnType } from "./Types";
import { InitFunction, Message, Nullable, UpdateFunction, UpdateMap, UpdateReturnType } from "./Types";

/**
* The return type of the `subscription` function.
Expand All @@ -20,7 +20,7 @@ type Subscription<TProps, TModel, TMessage> = (model: TModel, props: TProps) =>
* @template TModel The type of the model.
* @template TMessage The type of the messages discriminated union.
*/
interface UseElmishOptions<TProps, TModel, TMessage extends MessageBase> {
interface UseElmishOptions<TProps, TModel, TMessage extends Message> {
/**
* The name of the component. This is used for logging only.
* @type {string}
Expand Down Expand Up @@ -55,7 +55,7 @@ interface UseElmishOptions<TProps, TModel, TMessage extends MessageBase> {
* @example
* const [model, dispatch] = useElmish({ props, init, update, name: "MyComponent" });
*/
function useElmish<TProps, TModel, TMessage extends MessageBase> ({ name, props, init, update, subscription }: UseElmishOptions<TProps, TModel, TMessage>): [TModel, Dispatch<TMessage>] {
function useElmish<TProps, TModel, TMessage extends Message> ({ name, props, init, update, subscription }: UseElmishOptions<TProps, TModel, TMessage>): [TModel, Dispatch<TMessage>] {
let reentered = false;
const buffer: TMessage [] = [];
let currentModel: Partial<TModel> = {};
Expand Down Expand Up @@ -151,15 +151,15 @@ function useElmish<TProps, TModel, TMessage extends MessageBase> ({ name, props,
return [initializedModel, dispatch];
}

function callUpdate<TProps, TModel, TMessage extends MessageBase> (update: UpdateFunction<TProps, TModel, TMessage> | UpdateMap<TProps, TModel, TMessage>, msg: TMessage, model: TModel, props: TProps): UpdateReturnType<TModel, TMessage> {
function callUpdate<TProps, TModel, TMessage extends Message> (update: UpdateFunction<TProps, TModel, TMessage> | UpdateMap<TProps, TModel, TMessage>, msg: TMessage, model: TModel, props: TProps): UpdateReturnType<TModel, TMessage> {
if (typeof update === "function") {
return update(model, msg, props);
}

return callUpdateMap(update, msg, model, props);
}

function callUpdateMap<TProps, TModel, TMessage extends MessageBase> (updateMap: UpdateMap<TProps, TModel, TMessage>, msg: TMessage, model: TModel, props: TProps): UpdateReturnType<TModel, TMessage> {
function callUpdateMap<TProps, TModel, TMessage extends Message> (updateMap: UpdateMap<TProps, TModel, TMessage>, msg: TMessage, model: TModel, props: TProps): UpdateReturnType<TModel, TMessage> {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error -- We know that msg fits
const updateFn = updateMap[msg.name as TMessage["name"]] as (msg: TMessage, model: TModel, props: TProps) => UpdateReturnType<TModel, TMsg>;
Expand Down

0 comments on commit fd434f0

Please sign in to comment.