diff --git a/packages/client/src/components/MkButton.vue b/packages/client/src/components/MkButton.vue
index a0ff747afc..ab62fd166c 100644
--- a/packages/client/src/components/MkButton.vue
+++ b/packages/client/src/components/MkButton.vue
@@ -16,7 +16,7 @@
 		v-else
 		class="bghgjjyj _button"
 		:class="{ inline, primary, gradate, danger, rounded, full, mini }"
-		:to="to"
+		:to="to!"
 		@mousedown="onMousedown"
 	>
 		<div ref="ripples" class="ripples"></div>
@@ -36,6 +36,7 @@ const props = defineProps<{
 	gradate?: boolean;
 	rounded?: boolean;
 	inline?: boolean;
+	// FIXME: if `link`, `to` is necessary
 	link?: boolean;
 	to?: string;
 	autofocus?: boolean;
diff --git a/packages/client/src/components/MkChartTooltip.vue b/packages/client/src/components/MkChartTooltip.vue
index 659dc6d399..678a3ccdaa 100644
--- a/packages/client/src/components/MkChartTooltip.vue
+++ b/packages/client/src/components/MkChartTooltip.vue
@@ -28,11 +28,11 @@
 </template>
 
 <script lang="ts" setup>
-import {} from "vue";
+import type { Ref } from "vue";
 import MkTooltip from "./MkTooltip.vue";
 
 const props = defineProps<{
-	showing: boolean;
+	showing: Ref<boolean>;
 	x: number;
 	y: number;
 	title?: string;
diff --git a/packages/client/src/components/MkDialog.vue b/packages/client/src/components/MkDialog.vue
index 0b9d2cac36..c1f8f581a7 100644
--- a/packages/client/src/components/MkDialog.vue
+++ b/packages/client/src/components/MkDialog.vue
@@ -104,7 +104,7 @@
 			</MkInput>
 			<MkTextarea
 				v-if="input && input.type === 'paragraph'"
-				v-model="inputValue"
+				v-model="(inputValue as string)"
 				autofocus
 				type="paragraph"
 				:placeholder="input.placeholder || undefined"
@@ -204,7 +204,16 @@ import { i18n } from "@/i18n";
 import iconify from "@/scripts/icon";
 
 interface Input {
-	type: HTMLInputElement["type"];
+	type?:
+		| "text"
+		| "number"
+		| "password"
+		| "email"
+		| "url"
+		| "date"
+		| "time"
+		| "search"
+		| "paragraph";
 	placeholder?: string | null;
 	autocomplete?: string;
 	default: string | number | null;
@@ -237,8 +246,8 @@ const props = withDefaults(
 			| "question"
 			| "waiting"
 			| "search";
-		title: string;
-		text?: string;
+		title?: string | null;
+		text?: string | null;
 		isPlaintext?: boolean;
 		input?: Input;
 		select?: Select;
@@ -246,7 +255,7 @@ const props = withDefaults(
 		actions?: {
 			text: string;
 			primary?: boolean;
-			callback: (...args: any[]) => void;
+			callback: () => void;
 		}[];
 		showOkButton?: boolean;
 		showCancelButton?: boolean;
@@ -268,7 +277,10 @@ const props = withDefaults(
 );
 
 const emit = defineEmits<{
-	(ev: "done", v: { canceled: boolean; result: any }): void;
+	(
+		ev: "done",
+		v: { canceled: boolean; result?: string | number | boolean | null },
+	): void;
 	(ev: "closed"): void;
 }>();
 
@@ -306,7 +318,7 @@ const okButtonDisabled = computed<boolean>(() => {
 
 const inputEl = ref<typeof MkInput>();
 
-function done(canceled: boolean, result?) {
+function done(canceled: boolean, result?: string | number | boolean | null) {
 	emit("done", { canceled, result });
 	modal.value?.close(null);
 }
@@ -342,12 +354,12 @@ function onInputKeydown(evt: KeyboardEvent) {
 	}
 }
 
-function formatDateToYYYYMMDD(date) {
-	const year = date.getFullYear();
-	const month = ("0" + (date.getMonth() + 1)).slice(-2);
-	const day = ("0" + (date.getDate() + 1)).slice(-2);
-	return `${year}-${month}-${day}`;
-}
+// function formatDateToYYYYMMDD(date) {
+// 	const year = date.getFullYear();
+// 	const month = ("0" + (date.getMonth() + 1)).slice(-2);
+// 	const day = ("0" + (date.getDate() + 1)).slice(-2);
+// 	return `${year}-${month}-${day}`;
+// }
 
 /**
  * Appends a new search parameter to the value in the input field.
@@ -355,18 +367,18 @@ function formatDateToYYYYMMDD(date) {
  * begin typing a new criteria.
  * @param value The value to append.
  */
-function appendFilter(value: string) {
-	return (
-		[
-			typeof inputValue.value === "string"
-				? inputValue.value.trim()
-				: inputValue.value,
-			value,
-		]
-			.join(" ")
-			.trim() + " "
-	);
-}
+// function appendFilter(value: string) {
+// 	return (
+// 		[
+// 			typeof inputValue.value === "string"
+// 				? inputValue.value.trim()
+// 				: inputValue.value,
+// 			value,
+// 		]
+// 			.join(" ")
+// 			.trim() + " "
+// 	);
+// }
 
 onMounted(() => {
 	document.addEventListener("keydown", onKeydown);
diff --git a/packages/client/src/components/MkDrive.vue b/packages/client/src/components/MkDrive.vue
index 0273e0b40e..ad2f620f6c 100644
--- a/packages/client/src/components/MkDrive.vue
+++ b/packages/client/src/components/MkDrive.vue
@@ -253,7 +253,7 @@ function onStreamDriveFolderDeleted(folderId: string) {
 	removeFolder(folderId);
 }
 
-function onDragover(ev: DragEvent): any {
+function onDragover(ev: DragEvent) {
 	if (!ev.dataTransfer) return;
 
 	// ドラッグ元が自分自身の所有するアイテムだったら
@@ -285,7 +285,7 @@ function onDragleave() {
 	draghover.value = false;
 }
 
-function onDrop(ev: DragEvent): any {
+function onDrop(ev: DragEvent) {
 	draghover.value = false;
 
 	if (!ev.dataTransfer) return;
@@ -493,14 +493,12 @@ function move(target?: entities.DriveFolder) {
 	if (!target) {
 		goRoot();
 		return;
-	} else if (typeof target === "object") {
-		target = target.id;
 	}
 
 	fetching.value = true;
 
 	os.api("drive/folders/show", {
-		folderId: target,
+		folderId: target.id,
 	}).then((folderToMove) => {
 		folder.value = folderToMove;
 		hierarchyFolders.value = [];
diff --git a/packages/client/src/components/MkNotification.vue b/packages/client/src/components/MkNotification.vue
index b00646cdd6..6dd1a4b721 100644
--- a/packages/client/src/components/MkNotification.vue
+++ b/packages/client/src/components/MkNotification.vue
@@ -12,12 +12,12 @@
 				:user="notification.note.user"
 			/>
 			<MkAvatar
-				v-else-if="notification.user"
+				v-else-if="'user' in notification"
 				class="icon"
 				:user="notification.user"
 			/>
 			<img
-				v-else-if="notification.icon"
+				v-else-if="'icon' in notification && notification.icon"
 				class="icon"
 				:src="notification.icon"
 				alt=""
@@ -95,7 +95,7 @@
 					i18n.ts._notification.pollEnded
 				}}</span>
 				<MkA
-					v-else-if="notification.user"
+					v-else-if="'user' in notification"
 					v-user-preview="notification.user.id"
 					class="name"
 					:to="userPage(notification.user)"
@@ -133,7 +133,7 @@
 					:plain="true"
 					:nowrap="!full"
 					:lang="notification.note.lang"
-					:custom-emojis="notification.note.renote.emojis"
+					:custom-emojis="notification.note.renote!.emojis"
 				/>
 			</MkA>
 			<MkA
@@ -212,6 +212,7 @@
 				style="opacity: 0.7"
 				>{{ i18n.ts.youGotNewFollower }}
 				<div v-if="full && !hideFollowButton">
+					<!-- FIXME: Provide a UserDetailed here -->
 					<MkFollowButton
 						:user="notification.user"
 						:full="true"
@@ -269,7 +270,7 @@
 </template>
 
 <script lang="ts" setup>
-import { onMounted, onUnmounted, ref, watch } from "vue";
+import { onMounted, onUnmounted, ref, toRef, watch } from "vue";
 import type { entities } from "firefish-js";
 import XReactionIcon from "@/components/MkReactionIcon.vue";
 import MkFollowButton from "@/components/MkFollowButton.vue";
@@ -284,6 +285,8 @@ import { useTooltip } from "@/scripts/use-tooltip";
 import { defaultStore } from "@/store";
 import { instance } from "@/instance";
 import icon from "@/scripts/icon";
+import type { Connection } from "firefish-js/src/streaming";
+import type { Channels } from "firefish-js/src/streaming.types";
 
 const props = withDefaults(
 	defineProps<{
@@ -299,8 +302,8 @@ const props = withDefaults(
 
 const stream = useStream();
 
-const elRef = ref<HTMLElement>(null);
-const reactionRef = ref(null);
+const elRef = ref<HTMLElement | null>(null);
+const reactionRef = ref<InstanceType<typeof XReactionIcon> | null>(null);
 
 const hideFollowButton = defaultStore.state.hideFollowButtons;
 const showEmojiReactions =
@@ -311,7 +314,7 @@ const defaultReaction = ["⭐", "👍", "❤️"].includes(instance.defaultReact
 	: "⭐";
 
 let readObserver: IntersectionObserver | undefined;
-let connection;
+let connection: Connection<Channels["main"]> | null = null;
 
 onMounted(() => {
 	if (!props.notification.isRead) {
@@ -323,13 +326,13 @@ onMounted(() => {
 			observer.disconnect();
 		});
 
-		readObserver.observe(elRef.value);
+		readObserver.observe(elRef.value!);
 
 		connection = stream.useChannel("main");
-		connection.on("readAllNotifications", () => readObserver.disconnect());
+		connection.on("readAllNotifications", () => readObserver!.disconnect());
 
-		watch(props.notification.isRead, () => {
-			readObserver.disconnect();
+		watch(toRef(props.notification.isRead), () => {
+			readObserver!.disconnect();
 		});
 	}
 });
@@ -344,38 +347,47 @@ const groupInviteDone = ref(false);
 
 const acceptFollowRequest = () => {
 	followRequestDone.value = true;
-	os.api("following/requests/accept", { userId: props.notification.user.id });
+	os.api("following/requests/accept", {
+		userId: (props.notification as entities.ReceiveFollowRequestNotification)
+			.user.id,
+	});
 };
 
 const rejectFollowRequest = () => {
 	followRequestDone.value = true;
-	os.api("following/requests/reject", { userId: props.notification.user.id });
+	os.api("following/requests/reject", {
+		userId: (props.notification as entities.ReceiveFollowRequestNotification)
+			.user.id,
+	});
 };
 
 const acceptGroupInvitation = () => {
 	groupInviteDone.value = true;
 	os.apiWithDialog("users/groups/invitations/accept", {
-		invitationId: props.notification.invitation.id,
+		invitationId: (props.notification as entities.GroupInvitedNotification)
+			.invitation.id,
 	});
 };
 
 const rejectGroupInvitation = () => {
 	groupInviteDone.value = true;
 	os.api("users/groups/invitations/reject", {
-		invitationId: props.notification.invitation.id,
+		invitationId: (props.notification as entities.GroupInvitedNotification)
+			.invitation.id,
 	});
 };
 
 useTooltip(reactionRef, (showing) => {
+	const n = props.notification as entities.ReactionNotification;
 	os.popup(
 		XReactionTooltip,
 		{
 			showing,
-			reaction: props.notification.reaction
-				? props.notification.reaction.replace(/^:(\w+):$/, ":$1@.:")
-				: props.notification.reaction,
-			emojis: props.notification.note.emojis,
-			targetElement: reactionRef.value.$el,
+			reaction: n.reaction
+				? n.reaction.replace(/^:(\w+):$/, ":$1@.:")
+				: n.reaction,
+			emojis: n.note.emojis,
+			targetElement: reactionRef.value!.$el,
 		},
 		{},
 		"closed",
diff --git a/packages/client/src/components/MkPagePreview.vue b/packages/client/src/components/MkPagePreview.vue
index 034c6fed63..3a6a45745d 100644
--- a/packages/client/src/components/MkPagePreview.vue
+++ b/packages/client/src/components/MkPagePreview.vue
@@ -3,7 +3,7 @@
 		:to="`/@${page.user.username}/pages/${page.name}`"
 		class="vhpxefrj _block"
 		tabindex="-1"
-		:behavior="`${ui === 'deck' ? 'window' : null}`"
+		:behavior="ui === 'deck' ? 'window' : null"
 	>
 		<div
 			v-if="page.eyeCatchingImage"
@@ -36,9 +36,10 @@
 <script lang="ts" setup>
 import { userName } from "@/filters/user";
 import { ui } from "@/config";
+import type { entities } from "firefish-js";
 
 defineProps<{
-	page: any;
+	page: entities.Page;
 }>();
 </script>
 
diff --git a/packages/client/src/components/MkPageWindow.vue b/packages/client/src/components/MkPageWindow.vue
index d237f18091..082f9f0159 100644
--- a/packages/client/src/components/MkPageWindow.vue
+++ b/packages/client/src/components/MkPageWindow.vue
@@ -56,23 +56,22 @@ const router = new Router(routes, props.initialPath);
 
 const pageMetadata = ref<null | ComputedRef<PageMetadata>>();
 const windowEl = ref<InstanceType<typeof XWindow>>();
-const history = ref<{ path: string; key: any }[]>([
+const history = ref<{ path: string; key: string }[]>([
 	{
 		path: router.getCurrentPath(),
 		key: router.getCurrentKey(),
 	},
 ]);
 const buttonsLeft = computed(() => {
-	const buttons = [];
-
 	if (history.value.length > 1) {
-		buttons.push({
-			icon: `${icon("ph-caret-left")}`,
-			onClick: back,
-		});
+		return [
+			{
+				icon: `${icon("ph-caret-left")}`,
+				onClick: back,
+			},
+		];
 	}
-
-	return buttons;
+	return [];
 });
 const buttonsRight = computed(() => {
 	const buttons = [
@@ -114,7 +113,7 @@ const contextmenu = computed(() => [
 		text: i18n.ts.openInNewTab,
 		action: () => {
 			window.open(url + router.getCurrentPath(), "_blank");
-			windowEl.value.close();
+			windowEl.value!.close();
 		},
 	},
 	{
@@ -135,17 +134,17 @@ function back() {
 }
 
 function close() {
-	windowEl.value.close();
+	windowEl.value!.close();
 }
 
 function expand() {
 	mainRouter.push(router.getCurrentPath(), "forcePage");
-	windowEl.value.close();
+	windowEl.value!.close();
 }
 
 function popout() {
-	_popout(router.getCurrentPath(), windowEl.value.$el);
-	windowEl.value.close();
+	_popout(router.getCurrentPath(), windowEl.value!.$el);
+	windowEl.value!.close();
 }
 
 defineExpose({
diff --git a/packages/client/src/components/MkPollEditor.vue b/packages/client/src/components/MkPollEditor.vue
index 982a432e26..b05f80fafa 100644
--- a/packages/client/src/components/MkPollEditor.vue
+++ b/packages/client/src/components/MkPollEditor.vue
@@ -94,15 +94,14 @@ const props = defineProps<{
 	};
 }>();
 const emit = defineEmits<{
-	(
-		ev: "update:modelValue",
+	"update:modelValue": [
 		v: {
-			expiresAt: string;
-			expiredAfter: number;
+			expiresAt?: number;
+			expiredAfter?: number | null;
 			choices: string[];
 			multiple: boolean;
 		},
-	): void;
+	];
 }>();
 
 const choices = ref(props.modelValue.choices);
@@ -147,19 +146,19 @@ function get() {
 	};
 
 	const calcAfter = () => {
-		let base = Number.parseInt(after.value);
+		let base = Number.parseInt(after.value.toString());
 		switch (unit.value) {
+			// biome-ignore lint/suspicious/noFallthroughSwitchClause: Fallthrough intentially
 			case "day":
 				base *= 24;
-			// fallthrough
+			// biome-ignore lint/suspicious/noFallthroughSwitchClause: Fallthrough intentially
 			case "hour":
 				base *= 60;
-			// fallthrough
+			// biome-ignore lint/suspicious/noFallthroughSwitchClause: Fallthrough intentially
 			case "minute":
 				base *= 60;
-			// fallthrough
 			case "second":
-				return (base *= 1000);
+				return base * 1000;
 			default:
 				return null;
 		}
diff --git a/packages/client/src/components/MkPostForm.vue b/packages/client/src/components/MkPostForm.vue
index fb4a6dc740..2735eb55a3 100644
--- a/packages/client/src/components/MkPostForm.vue
+++ b/packages/client/src/components/MkPostForm.vue
@@ -1136,11 +1136,11 @@ async function post() {
 				nextTick(() => autosize.update(textareaEl.value));
 			});
 		})
-		.catch((err) => {
+		.catch((err: { message: string; id: string }) => {
 			posting.value = false;
 			os.alert({
 				type: "error",
-				text: err.message + "\n" + (err as any).id,
+				text: `${err.message}\n${err.id}`,
 			});
 		});
 	vibrate([10, 20, 10, 20, 10, 20, 60]);
diff --git a/packages/client/src/components/MkReactionIcon.vue b/packages/client/src/components/MkReactionIcon.vue
index e9d5a198cc..6608501478 100644
--- a/packages/client/src/components/MkReactionIcon.vue
+++ b/packages/client/src/components/MkReactionIcon.vue
@@ -9,9 +9,11 @@
 </template>
 
 <script lang="ts" setup>
+import type { entities } from "firefish-js";
+
 defineProps<{
 	reaction: string;
-	customEmojis?: any[]; // TODO
+	customEmojis?: entities.EmojiLite[];
 	noStyle?: boolean;
 }>();
 </script>
diff --git a/packages/client/src/components/MkReactionTooltip.vue b/packages/client/src/components/MkReactionTooltip.vue
index 0e83226c94..1286fc4c73 100644
--- a/packages/client/src/components/MkReactionTooltip.vue
+++ b/packages/client/src/components/MkReactionTooltip.vue
@@ -3,6 +3,7 @@
 		ref="tooltip"
 		:target-element="targetElement"
 		:max-width="340"
+		:showing="showing"
 		@closed="emit('closed')"
 	>
 		<div class="beeadbfb">
@@ -18,12 +19,15 @@
 </template>
 
 <script lang="ts" setup>
+import type { Ref } from "vue";
 import MkTooltip from "./MkTooltip.vue";
 import XReactionIcon from "@/components/MkReactionIcon.vue";
+import type { entities } from "firefish-js";
 
 defineProps<{
+	showing: Ref<boolean>;
 	reaction: string;
-	emojis: any[]; // TODO
+	emojis: entities.EmojiLite[];
 	targetElement: HTMLElement;
 }>();
 
diff --git a/packages/client/src/components/MkReactionsViewer.details.vue b/packages/client/src/components/MkReactionsViewer.details.vue
index 0d992ae431..14c2828d45 100644
--- a/packages/client/src/components/MkReactionsViewer.details.vue
+++ b/packages/client/src/components/MkReactionsViewer.details.vue
@@ -4,6 +4,7 @@
 		:target-element="targetElement"
 		:max-width="340"
 		@closed="emit('closed')"
+		:showing="showing"
 	>
 		<div class="bqxuuuey">
 			<div class="reaction">
@@ -29,15 +30,18 @@
 </template>
 
 <script lang="ts" setup>
+import type { Ref } from "vue";
 import MkTooltip from "./MkTooltip.vue";
 import XReactionIcon from "@/components/MkReactionIcon.vue";
+import type { entities } from "firefish-js";
 
 defineProps<{
+	showing: Ref<boolean>;
 	reaction: string;
-	users: any[]; // TODO
+	users: entities.User[]; // TODO
 	count: number;
-	emojis: any[]; // TODO
-	targetElement: HTMLElement;
+	emojis: entities.EmojiLite[]; // TODO
+	targetElement?: HTMLElement;
 }>();
 
 const emit = defineEmits<{
diff --git a/packages/client/src/components/MkReactionsViewer.reaction.vue b/packages/client/src/components/MkReactionsViewer.reaction.vue
index c403c7003c..89f51797ab 100644
--- a/packages/client/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/client/src/components/MkReactionsViewer.reaction.vue
@@ -89,7 +89,7 @@ useTooltip(
 				emojis: props.note.emojis,
 				users,
 				count: props.count,
-				targetElement: buttonRef.value,
+				targetElement: buttonRef.value!,
 			},
 			{},
 			"closed",
diff --git a/packages/client/src/components/MkRenoteButton.vue b/packages/client/src/components/MkRenoteButton.vue
index 845298b89d..7250757da4 100644
--- a/packages/client/src/components/MkRenoteButton.vue
+++ b/packages/client/src/components/MkRenoteButton.vue
@@ -46,7 +46,7 @@ const buttonRef = ref<HTMLElement>();
 const canRenote = computed(
 	() =>
 		["public", "home"].includes(props.note.visibility) ||
-		props.note.userId === me.id,
+		props.note.userId === me?.id,
 );
 
 useTooltip(buttonRef, async (showing) => {
diff --git a/packages/client/src/components/MkTooltip.vue b/packages/client/src/components/MkTooltip.vue
index 883067c51f..2ed3c1974b 100644
--- a/packages/client/src/components/MkTooltip.vue
+++ b/packages/client/src/components/MkTooltip.vue
@@ -5,13 +5,13 @@
 		@after-leave="emit('closed')"
 	>
 		<div
-			v-show="showing"
+			v-show="unref(showing)"
 			ref="el"
 			class="buebdbiu _acrylic _shadow"
 			:style="{ zIndex, maxWidth: maxWidth + 'px' }"
 		>
 			<slot>
-				<Mfm v-if="asMfm" :text="text" />
+				<Mfm v-if="asMfm" :text="text!" />
 				<span v-else>{{ text }}</span>
 			</slot>
 		</div>
@@ -19,15 +19,22 @@
 </template>
 
 <script lang="ts" setup>
-import { nextTick, onMounted, onUnmounted, ref } from "vue";
+import {
+	type MaybeRef,
+	nextTick,
+	onMounted,
+	onUnmounted,
+	ref,
+	unref,
+} from "vue";
 import * as os from "@/os";
 import { calcPopupPosition } from "@/scripts/popup-position";
 import { defaultStore } from "@/store";
 
 const props = withDefaults(
 	defineProps<{
-		showing: boolean;
-		targetElement?: HTMLElement;
+		showing: MaybeRef<boolean>;
+		targetElement?: HTMLElement | null;
 		x?: number;
 		y?: number;
 		text?: string;
@@ -40,6 +47,7 @@ const props = withDefaults(
 		maxWidth: 250,
 		direction: "top",
 		innerMargin: 0,
+		targetElement: null,
 	},
 );
 
@@ -51,7 +59,7 @@ const el = ref<HTMLElement>();
 const zIndex = os.claimZIndex("high");
 
 function setPosition() {
-	const data = calcPopupPosition(el.value, {
+	const data = calcPopupPosition(el.value!, {
 		anchorElement: props.targetElement,
 		direction: props.direction,
 		align: "center",
@@ -60,12 +68,12 @@ function setPosition() {
 		y: props.y,
 	});
 
-	el.value.style.transformOrigin = data.transformOrigin;
-	el.value.style.left = data.left + "px";
-	el.value.style.top = data.top + "px";
+	el.value!.style.transformOrigin = data.transformOrigin;
+	el.value!.style.left = `${data.left}px`;
+	el.value!.style.top = `${data.top}px`;
 }
 
-let loopHandler;
+let loopHandler: number;
 
 onMounted(() => {
 	nextTick(() => {
diff --git a/packages/client/src/components/MkUsersTooltip.vue b/packages/client/src/components/MkUsersTooltip.vue
index 25af3ac121..741194221d 100644
--- a/packages/client/src/components/MkUsersTooltip.vue
+++ b/packages/client/src/components/MkUsersTooltip.vue
@@ -4,6 +4,7 @@
 		:target-element="targetElement"
 		:max-width="250"
 		@closed="emit('closed')"
+		:showing="showing"
 	>
 		<div class="beaffaef">
 			<div v-for="u in users" :key="u.id" class="user">
@@ -18,12 +19,15 @@
 </template>
 
 <script lang="ts" setup>
+import type { Ref } from "vue";
 import MkTooltip from "./MkTooltip.vue";
+import type { entities } from "firefish-js";
 
 defineProps<{
-	users: any[]; // TODO
+	showing: Ref<boolean>;
+	users: entities.User[];
 	count: number;
-	targetElement: HTMLElement;
+	targetElement?: HTMLElement;
 }>();
 
 const emit = defineEmits<{
diff --git a/packages/client/src/components/MkWidgets.vue b/packages/client/src/components/MkWidgets.vue
index 1b1dafa522..fb8a449590 100644
--- a/packages/client/src/components/MkWidgets.vue
+++ b/packages/client/src/components/MkWidgets.vue
@@ -85,7 +85,7 @@ import icon from "@/scripts/icon";
 interface Widget {
 	name: string;
 	id: string;
-	data: Record<string, any>;
+	data: Record<string, unknown>;
 }
 
 const props = defineProps<{
@@ -137,12 +137,12 @@ function onContextmenu(widget: Widget, ev: MouseEvent) {
 			return isLink(el.parentElement);
 		}
 	};
-	if (isLink(ev.target)) return;
+	if (isLink(ev.target as HTMLElement)) return;
 	if (
 		["INPUT", "TEXTAREA", "IMG", "VIDEO", "CANVAS"].includes(
-			ev.target.tagName,
+			(ev.target as HTMLElement).tagName,
 		) ||
-		ev.target.attributes.contenteditable
+		(ev.target as HTMLElement).getAttribute("contentEditable")
 	)
 		return;
 	if (window.getSelection()?.toString() !== "") return;
diff --git a/packages/client/src/components/global/MkA.vue b/packages/client/src/components/global/MkA.vue
index b85774dced..fbe5472a24 100644
--- a/packages/client/src/components/global/MkA.vue
+++ b/packages/client/src/components/global/MkA.vue
@@ -22,7 +22,7 @@ import icon from "@/scripts/icon";
 
 const props = withDefaults(
 	defineProps<{
-		to?: string;
+		to: string;
 		activeClass?: null | string;
 		behavior?: null | "window" | "browser" | "modalWindow";
 	}>(),
diff --git a/packages/client/src/os.ts b/packages/client/src/os.ts
index 04c658ec89..ac1227b827 100644
--- a/packages/client/src/os.ts
+++ b/packages/client/src/os.ts
@@ -210,11 +210,13 @@ interface VueComponentConstructor<P, E> {
 	emits?: E;
 }
 
+type NonArrayAble<A> = A extends Array<unknown> ? never : A;
+
 export async function popup<Props, Emits>(
 	component: VueComponentConstructor<Props, Emits>,
-	props: Props & Record<string, unknown>,
-	events: Partial<Emits> = {},
-	disposeEvent?: string,
+	props: Props,
+	events: Partial<NonArrayAble<NonNullable<Emits>>> = {},
+	disposeEvent?: keyof Partial<NonArrayAble<NonNullable<Emits>>>,
 ) {
 	markRaw(component);
 
@@ -227,7 +229,7 @@ export async function popup<Props, Emits>(
 	};
 	const state = {
 		component,
-		props,
+		props: props as Record<string, unknown>,
 		events: disposeEvent
 			? {
 					...events,
diff --git a/packages/firefish-js/src/entities.ts b/packages/firefish-js/src/entities.ts
index a8a15f2290..5858dacb43 100644
--- a/packages/firefish-js/src/entities.ts
+++ b/packages/firefish-js/src/entities.ts
@@ -20,6 +20,16 @@ export type UserLite = {
 	movedToUri: any;
 	emojis: EmojiLite[];
 	instance?: InstanceLite;
+	avatarColor: null;
+	emojiModPerm: "unauthorized" | "add" | "mod" | "full";
+	isAdmin?: boolean;
+	isModerator?: boolean;
+	isBot?: boolean;
+	isLocked: boolean;
+	isIndexable: boolean;
+	isCat?: boolean;
+	speakAsCat?: boolean;
+	driveCapacityOverrideMb: number | null,
 };
 
 export type UserDetailed = UserLite & {
@@ -46,7 +56,6 @@ export type UserDetailed = UserLite & {
 	isCat: boolean;
 	isFollowed: boolean;
 	isFollowing: boolean;
-	isLocked: boolean;
 	isModerator: boolean;
 	isMuted: boolean;
 	isRenoteMuted: boolean;
@@ -228,7 +237,10 @@ export interface RenoteNotification extends BaseNotification {
 	type: "renote";
 	user: User;
 	userId: User["id"];
-	note: Note;
+	note: Note & {
+		renote: Note,
+		renoteId: string,
+	};
 }
 export interface QuoteNotification extends BaseNotification {
 	type: "quote";