Handle not having default rules better (#11214)

diff --git a/web-console/src/views/datasource-view/datasource-view.tsx b/web-console/src/views/datasource-view/datasource-view.tsx
index 3397c4a..8657a9a 100644
--- a/web-console/src/views/datasource-view/datasource-view.tsx
+++ b/web-console/src/views/datasource-view/datasource-view.tsx
@@ -112,6 +112,8 @@
   ],
 };
 
+const DEFAULT_RULES_KEY = '_default';
+
 function formatLoadDrop(segmentsToLoad: number, segmentsToDrop: number): string {
   const loadDrop: string[] = [];
   if (segmentsToLoad) {
@@ -167,6 +169,27 @@
   readonly avg_row_size: number;
 }
 
+function makeEmptyDatasourceQueryResultRow(datasource: string): DatasourceQueryResultRow {
+  return {
+    datasource,
+    num_segments: 0,
+    num_segments_to_load: 0,
+    num_segments_to_drop: 0,
+    minute_aligned_segments: 0,
+    hour_aligned_segments: 0,
+    day_aligned_segments: 0,
+    month_aligned_segments: 0,
+    year_aligned_segments: 0,
+    total_data_size: 0,
+    replicated_size: 0,
+    min_segment_rows: 0,
+    avg_segment_rows: 0,
+    max_segment_rows: 0,
+    total_rows: 0,
+    avg_row_size: 0,
+  };
+}
+
 function segmentGranularityCountsToRank(row: DatasourceQueryResultRow): number {
   return (
     Number(Boolean(row.num_segments)) +
@@ -185,6 +208,10 @@
   readonly unused?: boolean;
 }
 
+function makeUnusedDatasource(datasource: string): Datasource {
+  return { ...makeEmptyDatasourceQueryResultRow(datasource), rules: [], unused: true };
+}
+
 interface DatasourcesAndDefaultRules {
   readonly datasources: Datasource[];
   readonly defaultRules: Rule[];
@@ -384,11 +411,8 @@
         }
 
         if (!capabilities.hasCoordinatorAccess()) {
-          datasources.forEach((ds: any) => {
-            ds.rules = [];
-          });
           return {
-            datasources,
+            datasources: datasources.map(ds => ({ ...ds, rules: [] })),
             defaultRules: [],
           };
         }
@@ -397,43 +421,43 @@
 
         let unused: string[] = [];
         if (showUnused) {
-          const unusedResp = await Api.instance.get(
+          const unusedResp = await Api.instance.get<string[]>(
             '/druid/coordinator/v1/metadata/datasources?includeUnused',
           );
-          unused = unusedResp.data.filter((d: string) => !seen[d]);
+          unused = unusedResp.data.filter(d => !seen[d]);
         }
 
-        const rulesResp = await Api.instance.get('/druid/coordinator/v1/rules');
+        const rulesResp = await Api.instance.get<Record<string, Rule[]>>(
+          '/druid/coordinator/v1/rules',
+        );
         const rules = rulesResp.data;
 
-        const compactionConfigsResp = await Api.instance.get(
-          '/druid/coordinator/v1/config/compaction',
-        );
+        const compactionConfigsResp = await Api.instance.get<{
+          compactionConfigs: CompactionConfig[];
+        }>('/druid/coordinator/v1/config/compaction');
         const compactionConfigs = lookupBy(
           compactionConfigsResp.data.compactionConfigs || [],
-          (c: CompactionConfig) => c.dataSource,
+          c => c.dataSource,
         );
 
-        const compactionStatusesResp = await Api.instance.get(
+        const compactionStatusesResp = await Api.instance.get<{ latestStatus: CompactionStatus[] }>(
           '/druid/coordinator/v1/compaction/status',
         );
         const compactionStatuses = lookupBy(
           compactionStatusesResp.data.latestStatus || [],
-          (c: CompactionStatus) => c.dataSource,
+          c => c.dataSource,
         );
 
-        const allDatasources = (datasources as any).concat(
-          unused.map(d => ({ datasource: d, unused: true })),
-        );
-        allDatasources.forEach((ds: any) => {
-          ds.rules = rules[ds.datasource] || [];
-          ds.compactionConfig = compactionConfigs[ds.datasource];
-          ds.compactionStatus = compactionStatuses[ds.datasource];
-        });
-
         return {
-          datasources: allDatasources,
-          defaultRules: rules['_default'],
+          datasources: datasources.concat(unused.map(makeUnusedDatasource)).map(ds => {
+            return {
+              ...ds,
+              rules: rules[ds.datasource] || [],
+              compactionConfig: compactionConfigs[ds.datasource],
+              compactionStatus: compactionStatuses[ds.datasource],
+            };
+          }),
+          defaultRules: rules[DEFAULT_RULES_KEY] || [],
         };
       },
       onStateChange: datasourcesAndDefaultRulesState => {