From 3750e8050cb4a5707e76dfb8f49687cc0984f67f Mon Sep 17 00:00:00 2001
From: Renaud Chaput <renchap@gmail.com>
Date: Mon, 27 May 2024 11:24:59 +0200
Subject: [PATCH] Enable stricter Typescript options (#30435)

---
 app/javascript/entrypoints/public.tsx                | 12 ++++++------
 .../entrypoints/remote_interaction_helper.ts         |  9 ++++++++-
 .../mastodon/components/animated_number.tsx          |  5 +++--
 app/javascript/mastodon/components/hashtag_bar.tsx   |  7 +++++--
 app/javascript/mastodon/components/short_number.tsx  |  2 +-
 .../mastodon/features/emoji/emoji_mart_data_light.ts |  5 ++++-
 .../features/emoji/emoji_unicode_mapping_light.ts    |  5 ++++-
 app/javascript/mastodon/store/middlewares/sounds.ts  |  5 +++--
 tsconfig.json                                        |  2 ++
 9 files changed, 36 insertions(+), 16 deletions(-)

diff --git a/app/javascript/entrypoints/public.tsx b/app/javascript/entrypoints/public.tsx
index d45927226c..40a9b7c0ca 100644
--- a/app/javascript/entrypoints/public.tsx
+++ b/app/javascript/entrypoints/public.tsx
@@ -65,7 +65,7 @@ window.addEventListener('message', (e) => {
       {
         type: 'setHeight',
         id: data.id,
-        height: document.getElementsByTagName('html')[0].scrollHeight,
+        height: document.getElementsByTagName('html')[0]?.scrollHeight,
       },
       '*',
     );
@@ -135,7 +135,7 @@ function loaded() {
     );
   };
   const todayFormat = new IntlMessageFormat(
-    localeData['relative_format.today'] || 'Today at {time}',
+    localeData['relative_format.today'] ?? 'Today at {time}',
     locale,
   );
 
@@ -288,13 +288,13 @@ function loaded() {
       if (statusEl.dataset.spoiler === 'expanded') {
         statusEl.dataset.spoiler = 'folded';
         this.textContent = new IntlMessageFormat(
-          localeData['status.show_more'] || 'Show more',
+          localeData['status.show_more'] ?? 'Show more',
           locale,
         ).format() as string;
       } else {
         statusEl.dataset.spoiler = 'expanded';
         this.textContent = new IntlMessageFormat(
-          localeData['status.show_less'] || 'Show less',
+          localeData['status.show_less'] ?? 'Show less',
           locale,
         ).format() as string;
       }
@@ -316,8 +316,8 @@ function loaded() {
 
       const message =
         statusEl.dataset.spoiler === 'expanded'
-          ? localeData['status.show_less'] || 'Show less'
-          : localeData['status.show_more'] || 'Show more';
+          ? localeData['status.show_less'] ?? 'Show less'
+          : localeData['status.show_more'] ?? 'Show more';
       spoilerLink.textContent = new IntlMessageFormat(
         message,
         locale,
diff --git a/app/javascript/entrypoints/remote_interaction_helper.ts b/app/javascript/entrypoints/remote_interaction_helper.ts
index d5834c6c3d..419571c896 100644
--- a/app/javascript/entrypoints/remote_interaction_helper.ts
+++ b/app/javascript/entrypoints/remote_interaction_helper.ts
@@ -67,7 +67,9 @@ const fetchInteractionURLFailure = () => {
   );
 };
 
-const isValidDomain = (value: string) => {
+const isValidDomain = (value: unknown) => {
+  if (typeof value !== 'string') return false;
+
   const url = new URL('https:///path');
   url.hostname = value;
   return url.hostname === value;
@@ -124,6 +126,11 @@ const fromAcct = (acct: string) => {
   const domain = segments[1];
   const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;
 
+  if (!domain) {
+    fetchInteractionURLFailure();
+    return;
+  }
+
   axios
     .get(`https://${domain}/.well-known/webfinger`, {
       params: { resource: `acct:${acct}` },
diff --git a/app/javascript/mastodon/components/animated_number.tsx b/app/javascript/mastodon/components/animated_number.tsx
index e98e30b242..6c1e0aaec1 100644
--- a/app/javascript/mastodon/components/animated_number.tsx
+++ b/app/javascript/mastodon/components/animated_number.tsx
@@ -48,8 +48,9 @@ export const AnimatedNumber: React.FC<Props> = ({ value }) => {
             <span
               key={key}
               style={{
-                position: direction * style.y > 0 ? 'absolute' : 'static',
-                transform: `translateY(${style.y * 100}%)`,
+                position:
+                  direction * (style.y ?? 0) > 0 ? 'absolute' : 'static',
+                transform: `translateY(${(style.y ?? 0) * 100}%)`,
               }}
             >
               <ShortNumber value={data as number} />
diff --git a/app/javascript/mastodon/components/hashtag_bar.tsx b/app/javascript/mastodon/components/hashtag_bar.tsx
index ed5de7d3a5..1642ba6504 100644
--- a/app/javascript/mastodon/components/hashtag_bar.tsx
+++ b/app/javascript/mastodon/components/hashtag_bar.tsx
@@ -52,7 +52,10 @@ function uniqueHashtagsWithCaseHandling(hashtags: string[]) {
   );
 
   return Object.values(groups).map((tags) => {
-    if (tags.length === 1) return tags[0];
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- we know that the array has at least one element
+    const firstTag = tags[0]!;
+
+    if (tags.length === 1) return firstTag;
 
     // The best match is the one where we have the less difference between upper and lower case letter count
     const best = minBy(tags, (tag) => {
@@ -66,7 +69,7 @@ function uniqueHashtagsWithCaseHandling(hashtags: string[]) {
       return Math.abs(lowerCase - upperCase);
     });
 
-    return best ?? tags[0];
+    return best ?? firstTag;
   });
 }
 
diff --git a/app/javascript/mastodon/components/short_number.tsx b/app/javascript/mastodon/components/short_number.tsx
index 74c3c5d75e..a0b523aaad 100644
--- a/app/javascript/mastodon/components/short_number.tsx
+++ b/app/javascript/mastodon/components/short_number.tsx
@@ -48,7 +48,7 @@ const ShortNumberCounter: React.FC<ShortNumberCounterProps> = ({ value }) => {
 
   const count = (
     <FormattedNumber
-      value={rawNumber}
+      value={rawNumber ?? 0}
       maximumFractionDigits={maxFractionDigits}
     />
   );
diff --git a/app/javascript/mastodon/features/emoji/emoji_mart_data_light.ts b/app/javascript/mastodon/features/emoji/emoji_mart_data_light.ts
index ffca1f8b06..806a3f8927 100644
--- a/app/javascript/mastodon/features/emoji/emoji_mart_data_light.ts
+++ b/app/javascript/mastodon/features/emoji/emoji_mart_data_light.ts
@@ -29,7 +29,10 @@ const emojis: Emojis = {};
 
 // decompress
 Object.keys(shortCodesToEmojiData).forEach((shortCode) => {
-  const [_filenameData, searchData] = shortCodesToEmojiData[shortCode];
+  const emojiData = shortCodesToEmojiData[shortCode];
+  if (!emojiData) return;
+
+  const [_filenameData, searchData] = emojiData;
   const [native, short_names, search, unified] = searchData;
 
   emojis[shortCode] = {
diff --git a/app/javascript/mastodon/features/emoji/emoji_unicode_mapping_light.ts b/app/javascript/mastodon/features/emoji/emoji_unicode_mapping_light.ts
index 191419496f..d116c6c62c 100644
--- a/app/javascript/mastodon/features/emoji/emoji_unicode_mapping_light.ts
+++ b/app/javascript/mastodon/features/emoji/emoji_unicode_mapping_light.ts
@@ -46,7 +46,10 @@ function processEmojiMapData(
 Object.keys(shortCodesToEmojiData).forEach(
   (shortCode: ShortCodesToEmojiDataKey) => {
     if (shortCode === undefined) return;
-    const [filenameData, _searchData] = shortCodesToEmojiData[shortCode];
+
+    const emojiData = shortCodesToEmojiData[shortCode];
+    if (!emojiData) return;
+    const [filenameData, _searchData] = emojiData;
     filenameData.forEach((emojiMapData) => {
       processEmojiMapData(emojiMapData, shortCode);
     });
diff --git a/app/javascript/mastodon/store/middlewares/sounds.ts b/app/javascript/mastodon/store/middlewares/sounds.ts
index 720ee163e9..91407b1ec0 100644
--- a/app/javascript/mastodon/store/middlewares/sounds.ts
+++ b/app/javascript/mastodon/store/middlewares/sounds.ts
@@ -74,8 +74,9 @@ export const soundsMiddleware = (): Middleware<
     if (isActionWithMetaSound(action)) {
       const sound = action.meta.sound;
 
-      if (sound && Object.hasOwn(soundCache, sound)) {
-        play(soundCache[sound]);
+      if (sound) {
+        const s = soundCache[sound];
+        if (s) play(s);
       }
     }
 
diff --git a/tsconfig.json b/tsconfig.json
index 7010dda1fc..cc1f18a992 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -7,6 +7,8 @@
     "allowJs": true,
     "noEmit": true,
     "strict": true,
+    "noImplicitReturns": true,
+    "noUncheckedIndexedAccess": true,
     "esModuleInterop": true,
     "skipLibCheck": true,
     "baseUrl": "./",