From cf56cbf031eb075295d3bd952cfca2a452585238 Mon Sep 17 00:00:00 2001 From: foxriver76 Date: Tue, 12 Mar 2024 12:07:36 +0100 Subject: [PATCH 1/5] bring back type inference --- .../Vis/Widgets/Basic/BasicSpeechToText.tsx | 2 +- src/src/Vis/visBaseWidget.tsx | 10 +++---- src/src/Vis/visRxWidget.tsx | 26 +++++-------------- src/src/types.d.ts | 19 +++++++------- 4 files changed, 21 insertions(+), 36 deletions(-) diff --git a/src/src/Vis/Widgets/Basic/BasicSpeechToText.tsx b/src/src/Vis/Widgets/Basic/BasicSpeechToText.tsx index 7482af3b..37d12a0f 100644 --- a/src/src/Vis/Widgets/Basic/BasicSpeechToText.tsx +++ b/src/src/Vis/Widgets/Basic/BasicSpeechToText.tsx @@ -193,7 +193,7 @@ export default class BasicSpeechToText extends VisRxWidget { +class VisBaseWidget extends React.Component { static FORBIDDEN_CHARS = /[^._\-/ :!#$%&()+=@^{}|~]+/g; // from https://github.com/ioBroker/ioBroker.js-controller/blob/master/packages/common/lib/common/tools.js /** We do not store the SVG Element in the state because it is cyclic */ @@ -225,7 +225,7 @@ class VisBaseWidget extends React.Component, TState extends Record = Record> extends VisBaseWidget { - i18nPrefix?: string; - visHidden?: boolean; - adapter?: string; - version?: string; - url?: string; - custom?: any; - state: VisRxWidgetState & TState & { rxData: TRxData }; -} - -class VisRxWidget> extends VisBaseWidget { +class VisRxWidget, TState extends Record = Record> extends VisBaseWidget { static POSSIBLE_MUI_STYLES = POSSIBLE_MUI_STYLES; static i18nPrefix: string | undefined; @@ -430,7 +417,6 @@ class VisRxWidget> extends VisBaseWidget> extends VisBaseWidget !this.linkContext.IDs.includes(id)); - // @ts-expect-error check later if types wrong or call wrong if (unsubscribe.length) { + // @ts-expect-error check later if types wrong or call wrong await context.socket.unsubscribeState(unsubscribe, this.onStateChangedBind); } const subscribe = this.linkContext.IDs.filter(id => !oldIDs.includes(id)); - // @ts-expect-error check later if types wrong or call wrong if (subscribe.length) { + // @ts-expect-error check later if types wrong or call wrong await context.socket.subscribeState(subscribe, this.onStateChangedBind); } @@ -1026,7 +1012,7 @@ class VisRxWidget> extends VisBaseWidget { throw new Error('not implemented'); } } diff --git a/src/src/types.d.ts b/src/src/types.d.ts index 7c9695d7..7d6d1581 100644 --- a/src/src/types.d.ts +++ b/src/src/types.d.ts @@ -3,7 +3,7 @@ import type { Connection } from '@iobroker/adapter-react-v5'; import { CustomPaletteProperties, WidgetAttributeInfo, WidgetAttributesGroupInfo } from '@/Vis/visRxWidget'; import { CommonType } from '@iobroker/types/build/objects'; import { store } from '@/Store'; -import { RxWidgetAttributeType, RxWidgetInfoAttributesField } from '@/allInOneTypes'; +import { RxWidgetAttributeType, RxWidgetInfoAttributesField } from '@/detailedTypes'; import type moment from 'moment'; import VisFormatUtils from '@/Vis/visFormatUtils'; import VisView from '@/Vis/visView'; @@ -187,7 +187,7 @@ export interface ViewSettings { 'line-height'?: string; 'letter-spacing'?: string; 'word-spacing'?: string; - } + }; useAsDefault?: boolean; alwaysRender?: boolean; @@ -414,8 +414,8 @@ interface VisBindingOperationArgument { } interface VisBindingOperation { - op: VisBindingOperationType, - arg?: VisBindingOperationArgument[] | string | number | string[], + op: VisBindingOperationType; + arg?: VisBindingOperationArgument[] | string | number | string[]; formula?: string; } @@ -425,8 +425,8 @@ interface VisBinding { /** ioBroker state ID */ systemOid: StateID; /** Part of the string, like {id.ack} */ - token: string, - operations?: VisBindingOperation[], + token: string; + operations?: VisBindingOperation[]; format: string; isSeconds: boolean; } @@ -457,7 +457,7 @@ export interface CustomPaletteProperties { selectedView: string; themeType: 'dark' | 'light'; helpers: { - deviceIcons: Record + deviceIcons: Record; detectDevices: (socket: Connection) => Promise; getObjectIcon: (obj: ioBroker.Object, id?: string, imagePrefix?: string) => React.JSX.Element; allObjects: (socket: Connection) => Promise>; @@ -467,12 +467,11 @@ export interface CustomPaletteProperties { }; } - interface RxWidgetInfoAttributes { /** Name of the attributes section */ name: string; /** Fields of this attribute section */ - fields: RxWidgetInfoAttributesField[]; + fields: readonly RxWidgetInfoAttributesField[]; /** I18n Label */ label?: string; indexFrom?: number; @@ -503,7 +502,7 @@ interface RxWidgetInfo { visWidgetColor?: string; /** Groups of attributes */ - visAttrs: RxWidgetInfoAttributes[]; + visAttrs: (readonly RxWidgetInfoAttributes[]); /** Default style for widget */ visDefaultStyle?: React.CSSProperties; /** Position in the widget set */ From 49325926f4d84c536f6177dcf0c6651f2377eda3 Mon Sep 17 00:00:00 2001 From: foxriver76 Date: Tue, 12 Mar 2024 12:15:21 +0100 Subject: [PATCH 2/5] also infer state correctly for rxwidget --- src/src/Vis/visRxWidget.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/Vis/visRxWidget.tsx b/src/src/Vis/visRxWidget.tsx index 0a7f011d..3528eed7 100644 --- a/src/src/Vis/visRxWidget.tsx +++ b/src/src/Vis/visRxWidget.tsx @@ -116,7 +116,7 @@ interface VisRxWidgetState extends VisBaseWidgetState { disabled?: boolean; } -class VisRxWidget, TState extends Record = Record> extends VisBaseWidget { +class VisRxWidget, TState extends VisRxWidgetState = VisRxWidgetState> extends VisBaseWidget { static POSSIBLE_MUI_STYLES = POSSIBLE_MUI_STYLES; static i18nPrefix: string | undefined; From 753bff7e91abbd03035332b2ed58b966d2e2b408 Mon Sep 17 00:00:00 2001 From: foxriver76 Date: Tue, 12 Mar 2024 12:19:19 +0100 Subject: [PATCH 3/5] extend rxwidgetstate in single widgets --- src/src/Vis/Widgets/Basic/BasicSpeechToText.tsx | 3 ++- src/src/Vis/visBaseWidget.tsx | 2 +- src/src/Vis/visRxWidget.tsx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/src/Vis/Widgets/Basic/BasicSpeechToText.tsx b/src/src/Vis/Widgets/Basic/BasicSpeechToText.tsx index 37d12a0f..85e3ed63 100644 --- a/src/src/Vis/Widgets/Basic/BasicSpeechToText.tsx +++ b/src/src/Vis/Widgets/Basic/BasicSpeechToText.tsx @@ -7,11 +7,12 @@ import type { import { I18n } from '@iobroker/adapter-react-v5'; // @ts-expect-error fix import import type * as SpeechRecognition from 'dom-speech-recognition'; +import type { VisBaseWidgetState } from '@/Vis/visBaseWidget'; // eslint-disable-next-line no-use-before-define type RxData = GetRxDataFromWidget -interface BasicSpeechToTextState { +interface BasicSpeechToTextState extends VisBaseWidgetState { /** Current shown module text */ text: string; /** Current shown image */ diff --git a/src/src/Vis/visBaseWidget.tsx b/src/src/Vis/visBaseWidget.tsx index bdc11b7c..09e6faf3 100644 --- a/src/src/Vis/visBaseWidget.tsx +++ b/src/src/Vis/visBaseWidget.tsx @@ -154,7 +154,7 @@ interface VisBaseWidget { renderLastChange(style: unknown): React.ReactNode; } -class VisBaseWidget extends React.Component { +class VisBaseWidget = VisBaseWidgetState> extends React.Component { static FORBIDDEN_CHARS = /[^._\-/ :!#$%&()+=@^{}|~]+/g; // from https://github.com/ioBroker/ioBroker.js-controller/blob/master/packages/common/lib/common/tools.js /** We do not store the SVG Element in the state because it is cyclic */ diff --git a/src/src/Vis/visRxWidget.tsx b/src/src/Vis/visRxWidget.tsx index 3528eed7..349fabb9 100644 --- a/src/src/Vis/visRxWidget.tsx +++ b/src/src/Vis/visRxWidget.tsx @@ -116,7 +116,7 @@ interface VisRxWidgetState extends VisBaseWidgetState { disabled?: boolean; } -class VisRxWidget, TState extends VisRxWidgetState = VisRxWidgetState> extends VisBaseWidget { +class VisRxWidget, TState extends Partial = VisRxWidgetState> extends VisBaseWidget { static POSSIBLE_MUI_STYLES = POSSIBLE_MUI_STYLES; static i18nPrefix: string | undefined; From 3b86b7338d3ea56341abbaf10cb0de2a4870f35e Mon Sep 17 00:00:00 2001 From: foxriver76 Date: Tue, 12 Mar 2024 12:44:17 +0100 Subject: [PATCH 4/5] further cleanup --- src/src/Toolbar/WidgetImportDialog.tsx | 5 +++-- src/src/Vis/Widgets/Basic/BasicBar.tsx | 2 +- .../Vis/Widgets/Basic/BasicFilterDropdown.tsx | 10 ++++++---- src/src/Vis/Widgets/Basic/BasicIFrame.tsx | 18 +++--------------- src/src/Vis/Widgets/Basic/BasicImage.tsx | 16 +++------------- src/src/Vis/Widgets/Basic/BasicImage8.tsx | 2 +- src/src/Vis/Widgets/Basic/BasicRedNumber.tsx | 6 +++--- .../Widgets/Basic/BasicScreenResolution.tsx | 18 +++++++++--------- src/src/Vis/Widgets/Basic/BasicSvgShape.tsx | 2 +- src/src/Vis/visRxWidget.tsx | 2 +- src/src/types.d.ts | 2 +- 11 files changed, 32 insertions(+), 51 deletions(-) diff --git a/src/src/Toolbar/WidgetImportDialog.tsx b/src/src/Toolbar/WidgetImportDialog.tsx index 122e4279..e105f4a5 100644 --- a/src/src/Toolbar/WidgetImportDialog.tsx +++ b/src/src/Toolbar/WidgetImportDialog.tsx @@ -14,6 +14,7 @@ import { import { useFocus } from '@/Utils'; import { store } from '@/Store'; import { + AnyWidgetId, GroupWidget, GroupWidgetId, Project, Widget, } from '@/types'; import CustomAceEditor from '../Components/CustomAceEditor'; @@ -59,7 +60,7 @@ const WidgetImportDialog = (props: WidgetImportDialogProps) => { newWidgets[newKey] = widget; if (widget.grouped && widget.groupid && newWidgets[widget.groupid]?.data?.members) { // find group - const pos = (newWidgets[widget.groupid] as GroupWidget).data.members.indexOf(widget._id as string); + const pos = (newWidgets[widget.groupid] as GroupWidget).data.members.indexOf(widget._id as AnyWidgetId); if (pos !== -1) { (newWidgets[widget.groupid] as GroupWidget).data.members[pos] = newKey; } @@ -73,7 +74,7 @@ const WidgetImportDialog = (props: WidgetImportDialogProps) => { if (!isGroup(newWidgets[wid]) && props.selectedGroup !== undefined) { newWidgets[wid].grouped = true; newWidgets[wid].groupid = props.selectedGroup; - (project[props.selectedView].widgets[props.selectedGroup] as GroupWidget).data.members.push(wid); + (project[props.selectedView].widgets[props.selectedGroup] as GroupWidget).data.members.push(wid as AnyWidgetId); } }); diff --git a/src/src/Vis/Widgets/Basic/BasicBar.tsx b/src/src/Vis/Widgets/Basic/BasicBar.tsx index 26fb73fd..a480eba3 100644 --- a/src/src/Vis/Widgets/Basic/BasicBar.tsx +++ b/src/src/Vis/Widgets/Basic/BasicBar.tsx @@ -65,7 +65,7 @@ export default class BasicBar extends VisRxWidget { width: 200, height: 130, }, - }; + } as const; } /** diff --git a/src/src/Vis/Widgets/Basic/BasicFilterDropdown.tsx b/src/src/Vis/Widgets/Basic/BasicFilterDropdown.tsx index f8819004..24572cc1 100644 --- a/src/src/Vis/Widgets/Basic/BasicFilterDropdown.tsx +++ b/src/src/Vis/Widgets/Basic/BasicFilterDropdown.tsx @@ -25,11 +25,13 @@ import { Edit } from '@mui/icons-material'; import { I18n, Icon } from '@iobroker/adapter-react-v5'; -import { GetRxDataFromWidget, RxRenderWidgetProps, RxWidgetInfo } from '@/types'; +import { + GetRxDataFromWidget, RxRenderWidgetProps, RxWidgetInfo, WidgetData, +} from '@/types'; import VisRxWidget from '@/Vis/visRxWidget'; -import { WidgetData } from '@/types'; +import { RxWidgetInfoAttributesField, RxWidgetInfoCustomComponentProperties, RxWidgetInfoCustomComponentContext } from '@/allInOneTypes'; +import { VisWidgetCommand } from '@/Vis/visBaseWidget'; import FiltersEditorDialog from './FiltersEditorDialog'; -import { RxWidgetInfoAttributesField, RxWidgetInfoCustomComponentProperties, RxWidgetInfoCustomComponentContext } from "@/allInOneTypes"; // eslint-disable-next-line no-use-before-define type RxData = GetRxDataFromWidget @@ -281,7 +283,7 @@ class BasicFilterDropdown extends VisRxWidget { } } - onCommand(command: string): void { + onCommand(command: VisWidgetCommand): void { if (command === 'changeFilter') { // analyse filter this.forceUpdate(); diff --git a/src/src/Vis/Widgets/Basic/BasicIFrame.tsx b/src/src/Vis/Widgets/Basic/BasicIFrame.tsx index 259723e5..0fbde5df 100644 --- a/src/src/Vis/Widgets/Basic/BasicIFrame.tsx +++ b/src/src/Vis/Widgets/Basic/BasicIFrame.tsx @@ -4,21 +4,9 @@ import { GetRxDataFromWidget, RxRenderWidgetProps } from '@/types'; import VisRxWidget from '@/Vis/visRxWidget'; // eslint-disable-next-line no-use-before-define -// type RxData = GetRxDataFromWidget - -interface BasicIFrameData { - src: string; - refreshInterval: number; - refreshWithNoQuery: boolean; - noSandbox: boolean; - refreshOnViewChange: boolean; - refreshOnWakeUp: boolean; - scrollX: boolean; - scrollY: boolean; - seamless: boolean; -} +type RxData = GetRxDataFromWidget -export default class BasicIFrame extends VisRxWidget { +export default class BasicIFrame extends VisRxWidget { private refreshInterval: ReturnType | null = null; private readonly frameRef: React.RefObject; @@ -96,7 +84,7 @@ export default class BasicIFrame extends VisRxWidget { width: 600, height: 320, }, - }; + } as const; } async componentDidMount(): Promise { diff --git a/src/src/Vis/Widgets/Basic/BasicImage.tsx b/src/src/Vis/Widgets/Basic/BasicImage.tsx index d5fef574..08aa59a6 100644 --- a/src/src/Vis/Widgets/Basic/BasicImage.tsx +++ b/src/src/Vis/Widgets/Basic/BasicImage.tsx @@ -4,19 +4,9 @@ import { GetRxDataFromWidget, RxRenderWidgetProps } from '@/types'; import VisRxWidget from '@/Vis/visRxWidget'; // eslint-disable-next-line no-use-before-define -// type RxData = GetRxDataFromWidget - -interface BasicImageData { - src: string; - stretch: boolean; - refreshInterval: number; - refreshOnWakeUp: boolean; - refreshOnViewChange: boolean; - refreshWithNoQuery: boolean; - allowUserInteractions: boolean; -} +type RxData = GetRxDataFromWidget -export default class BasicImage extends VisRxWidget { +export default class BasicImage extends VisRxWidget { private refreshInterval: ReturnType | null = null; private readonly imageRef: React.RefObject; @@ -85,7 +75,7 @@ export default class BasicImage extends VisRxWidget { width: 200, height: 130, }, - }; + } as const; } async componentDidMount(): Promise { diff --git a/src/src/Vis/Widgets/Basic/BasicImage8.tsx b/src/src/Vis/Widgets/Basic/BasicImage8.tsx index bbbb1f00..1435150b 100644 --- a/src/src/Vis/Widgets/Basic/BasicImage8.tsx +++ b/src/src/Vis/Widgets/Basic/BasicImage8.tsx @@ -56,7 +56,7 @@ export default class BasicImage8 extends VisRxWidget { }, ], }], - }; + } as const; } /** diff --git a/src/src/Vis/Widgets/Basic/BasicRedNumber.tsx b/src/src/Vis/Widgets/Basic/BasicRedNumber.tsx index 24eee00a..cd1face0 100644 --- a/src/src/Vis/Widgets/Basic/BasicRedNumber.tsx +++ b/src/src/Vis/Widgets/Basic/BasicRedNumber.tsx @@ -85,7 +85,7 @@ export default class BasicRedNumber extends VisRxWidget { width: 52, height: 30, }, - }; + } as const; } /** @@ -123,7 +123,7 @@ export default class BasicRedNumber extends VisRxWidget { top: 0, left: 0, zIndex: 0, - color: this.state.rxData.backgroundColor || 'red', + color: this.state.rxData.background || 'red', }} />
{ borderColor: this.state.rxData.borderColor || 'white', borderWidth: 3, borderStyle: 'solid', - backgroundColor: this.state.rxData.backgroundColor || 'red', + backgroundColor: this.state.rxData.background || 'red', minWidth: 21, textAlign: 'center', color: props.style.color || 'white', diff --git a/src/src/Vis/Widgets/Basic/BasicScreenResolution.tsx b/src/src/Vis/Widgets/Basic/BasicScreenResolution.tsx index 822bdf4d..5dffeaa6 100644 --- a/src/src/Vis/Widgets/Basic/BasicScreenResolution.tsx +++ b/src/src/Vis/Widgets/Basic/BasicScreenResolution.tsx @@ -3,10 +3,9 @@ import React from 'react'; import { I18n } from '@iobroker/adapter-react-v5'; import { - RxRenderWidgetProps, VisLegacy, - RxWidgetState, RxWidgetProps, GetRxDataFromWidget, + RxRenderWidgetProps, VisLegacy, RxWidgetProps, GetRxDataFromWidget, } from '@/types'; -import VisRxWidget from '@/Vis/visRxWidget'; +import VisRxWidget, { VisRxWidgetState } from '@/Vis/visRxWidget'; declare global { interface Window { @@ -14,7 +13,7 @@ declare global { } } -interface BasicScreenResolutionState extends RxWidgetState { +interface BasicScreenResolutionState extends VisRxWidgetState { width: number; height: number; defaultView: string; @@ -30,9 +29,11 @@ export default class BasicScreenResolution extends VisRxWidget { @@ -122,7 +123,6 @@ export default class BasicScreenResolution extends VisRxWidget { width: 100, height: 100, }, - }; + } as const; } /** diff --git a/src/src/Vis/visRxWidget.tsx b/src/src/Vis/visRxWidget.tsx index 349fabb9..1aa7eef4 100644 --- a/src/src/Vis/visRxWidget.tsx +++ b/src/src/Vis/visRxWidget.tsx @@ -109,7 +109,7 @@ export interface CustomWidgetProperties { selectedWidget: AnyWidgetId; } -interface VisRxWidgetState extends VisBaseWidgetState { +export interface VisRxWidgetState extends VisBaseWidgetState { rxData: RxData; values: VisRxWidgetStateValues; visible?: boolean; diff --git a/src/src/types.d.ts b/src/src/types.d.ts index 7d6d1581..f8c74e29 100644 --- a/src/src/types.d.ts +++ b/src/src/types.d.ts @@ -613,4 +613,4 @@ type GetRxDataFromVisAttrs> = { } /** Infers the RxData from a given Widget */ -type GetRxDataFromWidget Record }> = GetRxDataFromVisAttrs> +type GetRxDataFromWidget Record }> = GetRxDataFromVisAttrs> From 2ffc08a078f5676f618f6b53adb865d59fca34a0 Mon Sep 17 00:00:00 2001 From: foxriver76 Date: Tue, 12 Mar 2024 13:00:16 +0100 Subject: [PATCH 5/5] add check-ts step to github ci --- .github/workflows/test-and-release.yml | 3 +++ package.json | 4 +++- src/tsconfig.json | 3 ++- tsconfig.json | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-and-release.yml b/.github/workflows/test-and-release.yml index e99dac7b..171ec0b0 100644 --- a/.github/workflows/test-and-release.yml +++ b/.github/workflows/test-and-release.yml @@ -40,6 +40,9 @@ jobs: - name: Build run: NODE_OPTIONS=--max_old_space_size=8192 npm run build + - name: Test TypeScript + run: npm run check-ts + - name: Run local tests run: npm run test-gui diff --git a/package.json b/package.json index 0d9cfced..b84205d8 100644 --- a/package.json +++ b/package.json @@ -35,12 +35,13 @@ "@alcalzone/release-script-plugin-iobroker": "^3.7.0", "@alcalzone/release-script-plugin-license": "^3.7.0", "@iobroker/vis-2-widgets-testing": "^1.0.0", - "@tsconfig/node16": "^16.1.1", + "@tsconfig/node18": "^18.2.2", "@types/mocha": "^10.0.6", "chai": "^4.3.10", "gulp": "^4.0.2", "iobroker.web": "*", "mocha": "^10.3.0", + "typescript": "^5.4.2", "unzipper": "^0.10.14" }, "bugs": { @@ -57,6 +58,7 @@ "main.js" ], "scripts": { + "check-ts": "tsc --project src/tsconfig.json", "start": "cd src && npm run start", "test": "mocha ./test/*.engine.js --exit", "test-gui": "mocha ./test/*.gui.js --exit", diff --git a/src/tsconfig.json b/src/tsconfig.json index c8e0b1e4..0c1702b6 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -1,9 +1,10 @@ { "extends": "../tsconfig.json", "compilerOptions": { + "module": "ES2020", "moduleResolution": "Bundler", "baseUrl": ".", - "checkJs": true, + "checkJs": false, "noEmit": false, "outDir": "./build", "sourceMap": true, diff --git a/tsconfig.json b/tsconfig.json index 358f6a88..c3ce1791 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ // Root tsconfig to set the settings and power editor support for all TS files { - "extends": "@tsconfig/node16/tsconfig.json", + "extends": "@tsconfig/node18/tsconfig.json", "compilerOptions": { // do not compile anything, this file is just to configure type checking "noEmit": true,