From d695d96e00a50ff27c7444f43866fd812d215ae9 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Fri, 2 Oct 2020 18:49:40 -0400 Subject: [PATCH] Various TypeScript improvements (#2309) - Use Mithril.Attributes as base for ComponentAttrs, remove =any from class signature for Component - Convert Alert to TypeScript, introduce AlertAttrs interface - Convert AlertManagerState to TypeScript, add overload signatures for `show`, introduce AlertState interface for stored Alerts. - Set ComponentAttrs as default T for Component - Make attrs in AlertAttrs optional - Add AlertIdentifier interface, simplify show type signature - Remove mithril patch shim, as all patches onto m are now deprecated - Use Mithril.Static for shim --- js/shims.d.ts | 12 +---- js/src/common/Component.ts | 8 +-- .../common/components/{Alert.js => Alert.tsx} | 30 +++++------ ...rtManagerState.js => AlertManagerState.ts} | 50 ++++++++++++------- 4 files changed, 53 insertions(+), 47 deletions(-) rename js/src/common/components/{Alert.js => Alert.tsx} (63%) rename js/src/common/states/{AlertManagerState.js => AlertManagerState.ts} (55%) diff --git a/js/shims.d.ts b/js/shims.d.ts index a2d31e316..a9fc278f3 100644 --- a/js/shims.d.ts +++ b/js/shims.d.ts @@ -1,6 +1,5 @@ // Mithril -import * as Mithril from 'mithril'; -import Stream from 'mithril/stream'; +import Mithril from 'mithril'; // Other third-party libs import * as _dayjs from 'dayjs'; @@ -9,13 +8,6 @@ import * as _$ from 'jquery'; // Globals from flarum/core import Application from './src/common/Application'; -/** - * Helpers that flarum/core patches into Mithril - */ -interface m extends Mithril.Static { - prop: typeof Stream; -} - /** * Export Mithril typings globally. * @@ -36,7 +28,7 @@ export as namespace Mithril; */ declare global { const $: typeof _$; - const m: m; + const m: Mithril.Static; const dayjs: typeof _dayjs; } diff --git a/js/src/common/Component.ts b/js/src/common/Component.ts index a9d5e91b9..3a19032a0 100644 --- a/js/src/common/Component.ts +++ b/js/src/common/Component.ts @@ -3,11 +3,7 @@ import * as Mithril from 'mithril'; let deprecatedPropsWarned = false; let deprecatedInitPropsWarned = false; -export type ComponentAttrs = { - className?: string; - - [key: string]: any; -}; +export interface ComponentAttrs extends Mithril.Attributes {} /** * The `Component` class defines a user interface 'building block'. A component @@ -36,7 +32,7 @@ export type ComponentAttrs = { * * @see https://mithril.js.org/components.html */ -export default abstract class Component implements Mithril.ClassComponent { +export default abstract class Component implements Mithril.ClassComponent { /** * The root DOM element for the component. */ diff --git a/js/src/common/components/Alert.js b/js/src/common/components/Alert.tsx similarity index 63% rename from js/src/common/components/Alert.js rename to js/src/common/components/Alert.tsx index d443a7ebe..bdebc89d1 100644 --- a/js/src/common/components/Alert.js +++ b/js/src/common/components/Alert.tsx @@ -1,31 +1,33 @@ -import Component from '../Component'; +import Component, { ComponentAttrs } from '../Component'; import Button from './Button'; import listItems from '../helpers/listItems'; import extract from '../utils/extract'; +import Mithril from 'mithril'; + +export interface AlertAttrs extends ComponentAttrs { + /** The type of alert this is. Will be used to give the alert a class name of `Alert--{type}`. */ + type?: string; + /** An array of controls to show in the alert. */ + controls?: Mithril.Children; + /** Whether or not the alert can be dismissed. */ + dismissible?: boolean; + /** A callback to run when the alert is dismissed */ + ondismiss?: Function; +} /** * The `Alert` component represents an alert box, which contains a message, * some controls, and may be dismissible. - * - * ### Attrs - * - * - `type` The type of alert this is. Will be used to give the alert a class - * name of `Alert--{type}`. - * - `controls` An array of controls to show in the alert. - * - `dismissible` Whether or not the alert can be dismissed. - * - `ondismiss` A callback to run when the alert is dismissed. - * - * All other attrs will be assigned as attributes on the DOM element. */ -export default class Alert extends Component { - view(vnode) { +export default class Alert extends Component { + view(vnode: Mithril.Vnode) { const attrs = Object.assign({}, this.attrs); const type = extract(attrs, 'type'); attrs.className = 'Alert Alert--' + type + ' ' + (attrs.className || ''); const content = extract(attrs, 'content') || vnode.children; - const controls = extract(attrs, 'controls') || []; + const controls = (extract(attrs, 'controls') || []) as Mithril.ChildArray; // If the alert is meant to be dismissible (which is the case by default), // then we will create a dismiss button to append as the final control in diff --git a/js/src/common/states/AlertManagerState.js b/js/src/common/states/AlertManagerState.ts similarity index 55% rename from js/src/common/states/AlertManagerState.js rename to js/src/common/states/AlertManagerState.ts index 19820b4fc..a7a8c2278 100644 --- a/js/src/common/states/AlertManagerState.js +++ b/js/src/common/states/AlertManagerState.ts @@ -1,10 +1,20 @@ -import Alert from '../components/Alert'; +import Mithril from 'mithril'; +import Alert, { AlertAttrs } from '../components/Alert'; + +/** + * Returned by `AlertManagerState.show`. Used to dismiss alerts. + */ +export type AlertIdentifier = number; + +export interface AlertState { + componentClass: typeof Alert; + attrs: AlertAttrs; + children: Mithril.Children; +} export default class AlertManagerState { - constructor() { - this.activeAlerts = {}; - this.alertId = 0; - } + protected activeAlerts: { [id: number]: AlertState } = {}; + protected alertId = 0; getActiveAlerts() { return this.activeAlerts; @@ -12,19 +22,27 @@ export default class AlertManagerState { /** * Show an Alert in the alerts area. + * + * @returns The alert's ID, which can be used to dismiss the alert. */ - show(arg1, arg2, arg3) { + show(children: Mithril.Children): AlertIdentifier; + show(attrs: AlertAttrs, children: Mithril.Children): AlertIdentifier; + show(componentClass: Alert, attrs: AlertAttrs, children: Mithril.Children): AlertIdentifier; + + show(arg1: any, arg2?: any, arg3?: any) { + // Assigns variables as per the above signatures let componentClass = Alert; - let attrs = {}; - let children; + let attrs: AlertAttrs = {}; + let children: Mithril.Children; + if (arguments.length == 1) { - children = arg1; + children = arg1 as Mithril.Children; } else if (arguments.length == 2) { - attrs = arg1; - children = arg2; + attrs = arg1 as AlertAttrs; + children = arg2 as Mithril.Children; } else if (arguments.length == 3) { - componentClass = arg1; - attrs = arg2; + componentClass = arg1 as typeof Alert; + attrs = arg2 as AlertAttrs; children = arg3; } @@ -45,7 +63,7 @@ export default class AlertManagerState { /** * Dismiss an alert. */ - dismiss(key) { + dismiss(key: AlertIdentifier): void { if (!key || !(key in this.activeAlerts)) return; delete this.activeAlerts[key]; @@ -54,10 +72,8 @@ export default class AlertManagerState { /** * Clear all alerts. - * - * @public */ - clear() { + clear(): void { this.activeAlerts = {}; m.redraw(); }