From bb3d274db64fe662ad01780ca5eadbc263f6f759 Mon Sep 17 00:00:00 2001
From: Kagami Sascha Rosylight <saschanaz@outlook.com>
Date: Sun, 18 Dec 2022 13:13:05 +0900
Subject: [PATCH] refactor(client): add proper types to `never[]` (#9340)

---
 packages/client/src/components/mfm.ts         |  9 ++--
 packages/client/src/scripts/array.ts          |  2 +-
 .../client/src/scripts/collect-page-vars.ts   | 38 +++++++++++++----
 packages/client/src/scripts/physics.ts        | 16 ++++----
 packages/client/src/store.ts                  | 41 ++++++++++++++++---
 5 files changed, 78 insertions(+), 28 deletions(-)

diff --git a/packages/client/src/components/mfm.ts b/packages/client/src/components/mfm.ts
index 688857a499..5b5b1caae3 100644
--- a/packages/client/src/components/mfm.ts
+++ b/packages/client/src/components/mfm.ts
@@ -54,13 +54,13 @@ export default defineComponent({
 			return t.match(/^[0-9.]+s$/) ? t : null;
 		};
 
-		const genEl = (ast: mfm.MfmNode[]) => concat(ast.map((token): VNode[] => {
+		const genEl = (ast: mfm.MfmNode[]) => ast.map((token): VNode | string | (VNode | string)[] => {
 			switch (token.type) {
 				case 'text': {
 					const text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n');
 
 					if (!this.plain) {
-						const res = [];
+						const res: (VNode | string)[] = [];
 						for (const t of text.split('\n')) {
 							res.push(h('br'));
 							res.push(t);
@@ -317,12 +317,13 @@ export default defineComponent({
 				}
 
 				default: {
-					console.error('unrecognized ast type:', token.type);
+					// eslint-disable-next-line @typescript-eslint/no-explicit-any
+					console.error('unrecognized ast type:', (token as any).type);
 
 					return [];
 				}
 			}
-		}));
+		}).flat(Infinity) as (VNode | string)[];
 
 		// Parse ast to DOM
 		return h('span', genEl(ast));
diff --git a/packages/client/src/scripts/array.ts b/packages/client/src/scripts/array.ts
index 26c6195d66..4620c8b735 100644
--- a/packages/client/src/scripts/array.ts
+++ b/packages/client/src/scripts/array.ts
@@ -123,7 +123,7 @@ export function lessThan(xs: number[], ys: number[]): boolean {
  * Returns the longest prefix of elements that satisfy the predicate
  */
 export function takeWhile<T>(f: Predicate<T>, xs: T[]): T[] {
-	const ys = [];
+	const ys: T[] = [];
 	for (const x of xs) {
 		if (f(x)) {
 			ys.push(x);
diff --git a/packages/client/src/scripts/collect-page-vars.ts b/packages/client/src/scripts/collect-page-vars.ts
index a4096fb2c2..76b68beaf6 100644
--- a/packages/client/src/scripts/collect-page-vars.ts
+++ b/packages/client/src/scripts/collect-page-vars.ts
@@ -1,42 +1,62 @@
-export function collectPageVars(content) {
-	const pageVars = [];
-	const collect = (xs: any[]) => {
+interface StringPageVar {
+	name: string,
+	type: 'string',
+	value: string
+}
+
+interface NumberPageVar {
+	name: string,
+	type: 'number',
+	value: number
+}
+
+interface BooleanPageVar {
+	name: string,
+	type: 'boolean',
+	value: boolean
+}
+
+type PageVar = StringPageVar | NumberPageVar | BooleanPageVar;
+
+export function collectPageVars(content): PageVar[] {
+	const pageVars: PageVar[] = [];
+	const collect = (xs: any[]): void => {
 		for (const x of xs) {
 			if (x.type === 'textInput') {
 				pageVars.push({
 					name: x.name,
 					type: 'string',
-					value: x.default || ''
+					value: x.default || '',
 				});
 			} else if (x.type === 'textareaInput') {
 				pageVars.push({
 					name: x.name,
 					type: 'string',
-					value: x.default || ''
+					value: x.default || '',
 				});
 			} else if (x.type === 'numberInput') {
 				pageVars.push({
 					name: x.name,
 					type: 'number',
-					value: x.default || 0
+					value: x.default || 0,
 				});
 			} else if (x.type === 'switch') {
 				pageVars.push({
 					name: x.name,
 					type: 'boolean',
-					value: x.default || false
+					value: x.default || false,
 				});
 			} else if (x.type === 'counter') {
 				pageVars.push({
 					name: x.name,
 					type: 'number',
-					value: 0
+					value: 0,
 				});
 			} else if (x.type === 'radioButton') {
 				pageVars.push({
 					name: x.name,
 					type: 'string',
-					value: x.default || ''
+					value: x.default || '',
 				});
 			} else if (x.children) {
 				collect(x.children);
diff --git a/packages/client/src/scripts/physics.ts b/packages/client/src/scripts/physics.ts
index 9e657906c2..f0a5b0fdd6 100644
--- a/packages/client/src/scripts/physics.ts
+++ b/packages/client/src/scripts/physics.ts
@@ -55,13 +55,13 @@ export function physics(container: HTMLElement) {
 		//wallLeft,
 	]);
 
-	const objEls = Array.from(container.children);
-	const objs = [];
+	const objEls = Array.from(container.children) as HTMLElement[];
+	const objs: Matter.Body[] = [];
 	for (const objEl of objEls) {
 		const left = objEl.dataset.physicsX ? parseInt(objEl.dataset.physicsX) : objEl.offsetLeft;
 		const top = objEl.dataset.physicsY ? parseInt(objEl.dataset.physicsY) : objEl.offsetTop;
 
-		let obj;
+		let obj: Matter.Body;
 		if (objEl.classList.contains('_physics_circle_')) {
 			obj = Matter.Bodies.circle(
 				left + (objEl.offsetWidth / 2),
@@ -84,7 +84,7 @@ export function physics(container: HTMLElement) {
 				}
 			);
 		}
-		objEl.id = obj.id;
+		objEl.id = obj.id.toString();
 		objs.push(obj);
 	}
 
@@ -109,10 +109,10 @@ export function physics(container: HTMLElement) {
 	render.mouse = mouse;
 
 	for (const objEl of objEls) {
-		objEl.style.position = `absolute`;
-		objEl.style.top = 0;
-		objEl.style.left = 0;
-		objEl.style.margin = 0;
+		objEl.style.position = 'absolute';
+		objEl.style.top = '0';
+		objEl.style.left = '0';
+		objEl.style.margin = '0';
 	}
 
 	window.requestAnimationFrame(update);
diff --git a/packages/client/src/store.ts b/packages/client/src/store.ts
index 3971214af0..061e1913d6 100644
--- a/packages/client/src/store.ts
+++ b/packages/client/src/store.ts
@@ -2,11 +2,34 @@ import { markRaw, ref } from 'vue';
 import { Storage } from './pizzax';
 import { Theme } from './scripts/theme';
 
-export const postFormActions = [];
-export const userActions = [];
-export const noteActions = [];
-export const noteViewInterruptors = [];
-export const notePostInterruptors = [];
+interface PostFormAction {
+	title: string,
+	handler: <T>(form: T, update: (key: unknown, value: unknown) => void) => void;
+}
+
+interface UserAction {
+	title: string,
+	handler: (user: UserDetailed) => void;
+}
+
+interface NoteAction {
+	title: string,
+	handler: (note: Note) => void;
+}
+
+interface NoteViewInterruptor {
+	handler: (note: Note) => unknown;
+}
+
+interface NotePostInterruptor {
+	handler: (note: FIXME) => unknown;
+}
+
+export const postFormActions: PostFormAction[] = [];
+export const userActions: UserAction[] = [];
+export const noteActions: NoteAction[] = [];
+export const noteViewInterruptors: NoteViewInterruptor[] = [];
+export const notePostInterruptors: NotePostInterruptor[] = [];
 
 // TODO: それぞれいちいちwhereとかdefaultというキーを付けなきゃいけないの冗長なのでなんとかする(ただ型定義が面倒になりそう)
 //       あと、現行の定義の仕方なら「whereが何であるかに関わらずキー名の重複不可」という制約を付けられるメリットもあるからそのメリットを引き継ぐ方法も考えないといけない
@@ -266,11 +289,17 @@ type Plugin = {
 	ast: any[];
 };
 
+interface Watcher {
+	key: string;
+	callback: (value: unknown) => void;
+}
+
 /**
  * 常にメモリにロードしておく必要がないような設定情報を保管するストレージ(非リアクティブ)
  */
 import lightTheme from '@/themes/l-light.json5';
 import darkTheme from '@/themes/d-green-lime.json5';
+import { Note, UserDetailed } from 'misskey-js/built/entities';
 
 export class ColdDeviceStorage {
 	public static default = {
@@ -289,7 +318,7 @@ export class ColdDeviceStorage {
 		sound_channel: { type: 'syuilo/square-pico', volume: 1 },
 	};
 
-	public static watchers = [];
+	public static watchers: Watcher[] = [];
 
 	public static get<T extends keyof typeof ColdDeviceStorage.default>(key: T): typeof ColdDeviceStorage.default[T] {
 		// TODO: indexedDBにする
-- 
GitLab