From b912e4f911dd7ddcc6a28cf73bb3aa26936ec15a Mon Sep 17 00:00:00 2001
From: Alexander Polyankin <alexander.polyankin@metabase.com>
Date: Tue, 7 Dec 2021 16:15:04 +0300
Subject: [PATCH] Convert homepage to TypeScript (#19204)

---
 .../home/homepage/{actions.js => actions.ts}  |  6 +-
 ...tyled.jsx => CollectionSection.styled.tsx} |  2 +-
 ...ctionSection.jsx => CollectionSection.tsx} | 30 +++++-----
 ...pec.js => CollectionSection.unit.spec.tsx} | 17 +++---
 .../CollectionSection/{index.js => index.ts}  |  0
 ....styled.jsx => DatabaseSection.styled.tsx} |  4 +-
 ...atabaseSection.jsx => DatabaseSection.tsx} | 40 ++++++-------
 ....spec.js => DatabaseSection.unit.spec.tsx} | 27 +++++----
 .../DatabaseSection/{index.js => index.ts}    |  0
 ....styled.jsx => GreetingSection.styled.tsx} |  0
 ...reetingSection.jsx => GreetingSection.tsx} | 14 ++---
 ....spec.js => GreetingSection.unit.spec.tsx} |  8 ++-
 .../GreetingSection/{index.js => index.ts}    |  0
 ...omepage.styled.jsx => Homepage.styled.tsx} |  0
 .../Homepage/{Homepage.jsx => Homepage.tsx}   | 40 +++++++------
 .../Homepage/{index.js => index.ts}           |  0
 ...{Section.styled.jsx => Section.styled.tsx} |  2 +-
 .../components/Section/{index.js => index.ts} |  0
 ...ion.styled.jsx => StartSection.styled.tsx} | 10 +++-
 .../{StartSection.jsx => StartSection.tsx}    | 48 +++++++---------
 ...nit.spec.js => StartSection.unit.spec.tsx} | 54 ++++++++++--------
 .../StartSection/{index.js => index.ts}       |  0
 ...tion.styled.jsx => XraySection.styled.tsx} |  4 +-
 .../{XraySection.jsx => XraySection.tsx}      | 57 +++++++++----------
 ...unit.spec.js => XraySection.unit.spec.tsx} | 35 ++++++++++--
 .../XraySection/{index.js => index.ts}        |  0
 .../{HomepageApp.jsx => HomepageApp.tsx}      |  5 +-
 .../HomepageApp/{index.js => index.ts}        |  0
 .../homepage/{selectors.js => selectors.ts}   |  2 +-
 frontend/src/metabase/home/homepage/types.ts  | 29 ++++++++++
 jest.unit.conf.json                           |  4 +-
 31 files changed, 251 insertions(+), 187 deletions(-)
 rename frontend/src/metabase/home/homepage/{actions.js => actions.ts} (87%)
 rename frontend/src/metabase/home/homepage/components/CollectionSection/{CollectionSection.styled.jsx => CollectionSection.styled.tsx} (100%)
 rename frontend/src/metabase/home/homepage/components/CollectionSection/{CollectionSection.jsx => CollectionSection.tsx} (81%)
 rename frontend/src/metabase/home/homepage/components/CollectionSection/{CollectionSection.unit.spec.js => CollectionSection.unit.spec.tsx} (81%)
 rename frontend/src/metabase/home/homepage/components/CollectionSection/{index.js => index.ts} (100%)
 rename frontend/src/metabase/home/homepage/components/DatabaseSection/{DatabaseSection.styled.jsx => DatabaseSection.styled.tsx} (100%)
 rename frontend/src/metabase/home/homepage/components/DatabaseSection/{DatabaseSection.jsx => DatabaseSection.tsx} (79%)
 rename frontend/src/metabase/home/homepage/components/DatabaseSection/{DatabaseSection.unit.spec.js => DatabaseSection.unit.spec.tsx} (84%)
 rename frontend/src/metabase/home/homepage/components/DatabaseSection/{index.js => index.ts} (100%)
 rename frontend/src/metabase/home/homepage/components/GreetingSection/{GreetingSection.styled.jsx => GreetingSection.styled.tsx} (100%)
 rename frontend/src/metabase/home/homepage/components/GreetingSection/{GreetingSection.jsx => GreetingSection.tsx} (79%)
 rename frontend/src/metabase/home/homepage/components/GreetingSection/{GreetingSection.unit.spec.js => GreetingSection.unit.spec.tsx} (67%)
 rename frontend/src/metabase/home/homepage/components/GreetingSection/{index.js => index.ts} (100%)
 rename frontend/src/metabase/home/homepage/components/Homepage/{Homepage.styled.jsx => Homepage.styled.tsx} (100%)
 rename frontend/src/metabase/home/homepage/components/Homepage/{Homepage.jsx => Homepage.tsx} (75%)
 rename frontend/src/metabase/home/homepage/components/Homepage/{index.js => index.ts} (100%)
 rename frontend/src/metabase/home/homepage/components/Section/{Section.styled.jsx => Section.styled.tsx} (100%)
 rename frontend/src/metabase/home/homepage/components/Section/{index.js => index.ts} (100%)
 rename frontend/src/metabase/home/homepage/components/StartSection/{StartSection.styled.jsx => StartSection.styled.tsx} (95%)
 rename frontend/src/metabase/home/homepage/components/StartSection/{StartSection.jsx => StartSection.tsx} (84%)
 rename frontend/src/metabase/home/homepage/components/StartSection/{StartSection.unit.spec.js => StartSection.unit.spec.tsx} (84%)
 rename frontend/src/metabase/home/homepage/components/StartSection/{index.js => index.ts} (100%)
 rename frontend/src/metabase/home/homepage/components/XraySection/{XraySection.styled.jsx => XraySection.styled.tsx} (100%)
 rename frontend/src/metabase/home/homepage/components/XraySection/{XraySection.jsx => XraySection.tsx} (72%)
 rename frontend/src/metabase/home/homepage/components/XraySection/{XraySection.unit.spec.js => XraySection.unit.spec.tsx} (82%)
 rename frontend/src/metabase/home/homepage/components/XraySection/{index.js => index.ts} (100%)
 rename frontend/src/metabase/home/homepage/containers/HomepageApp/{HomepageApp.jsx => HomepageApp.tsx} (92%)
 rename frontend/src/metabase/home/homepage/containers/HomepageApp/{index.js => index.ts} (100%)
 rename frontend/src/metabase/home/homepage/{selectors.js => selectors.ts} (88%)
 create mode 100644 frontend/src/metabase/home/homepage/types.ts

diff --git a/frontend/src/metabase/home/homepage/actions.js b/frontend/src/metabase/home/homepage/actions.ts
similarity index 87%
rename from frontend/src/metabase/home/homepage/actions.js
rename to frontend/src/metabase/home/homepage/actions.ts
index e689063a9f5..c7003717d78 100644
--- a/frontend/src/metabase/home/homepage/actions.js
+++ b/frontend/src/metabase/home/homepage/actions.ts
@@ -3,7 +3,7 @@ import { updateSetting } from "metabase/admin/settings/settings";
 
 export const HIDE_DATA = "metabase/home/homepage/HIDE_DATA";
 export const hideData = createThunkAction(HIDE_DATA, function() {
-  return async function(dispatch) {
+  return async function(dispatch: any) {
     const setting = { key: "show-homepage-data", value: false };
     await dispatch(updateSetting(setting));
   };
@@ -11,7 +11,7 @@ export const hideData = createThunkAction(HIDE_DATA, function() {
 
 export const HIDE_XRAYS = "metabase/home/homepage/HIDE_XRAYS";
 export const hideXrays = createThunkAction(HIDE_XRAYS, function() {
-  return async function(dispatch) {
+  return async function(dispatch: any) {
     const setting = { key: "show-homepage-xrays", value: false };
     await dispatch(updateSetting(setting));
   };
@@ -19,7 +19,7 @@ export const hideXrays = createThunkAction(HIDE_XRAYS, function() {
 
 export const HIDE_PIN_MESSAGE = "metabase/home/homepage/HIDE_PIN_MESSAGE";
 export const hidePinMessage = createThunkAction(HIDE_PIN_MESSAGE, function() {
-  return async function(dispatch) {
+  return async function(dispatch: any) {
     const setting = { key: "show-homepage-pin-message", value: false };
     await dispatch(updateSetting(setting));
   };
diff --git a/frontend/src/metabase/home/homepage/components/CollectionSection/CollectionSection.styled.jsx b/frontend/src/metabase/home/homepage/components/CollectionSection/CollectionSection.styled.tsx
similarity index 100%
rename from frontend/src/metabase/home/homepage/components/CollectionSection/CollectionSection.styled.jsx
rename to frontend/src/metabase/home/homepage/components/CollectionSection/CollectionSection.styled.tsx
index 0c96efa2ee9..7b9c490799c 100644
--- a/frontend/src/metabase/home/homepage/components/CollectionSection/CollectionSection.styled.jsx
+++ b/frontend/src/metabase/home/homepage/components/CollectionSection/CollectionSection.styled.tsx
@@ -1,5 +1,5 @@
-import styled from "styled-components";
 import { Link } from "react-router";
+import styled from "styled-components";
 import { color } from "metabase/lib/colors";
 import Icon from "metabase/components/Icon";
 
diff --git a/frontend/src/metabase/home/homepage/components/CollectionSection/CollectionSection.jsx b/frontend/src/metabase/home/homepage/components/CollectionSection/CollectionSection.tsx
similarity index 81%
rename from frontend/src/metabase/home/homepage/components/CollectionSection/CollectionSection.jsx
rename to frontend/src/metabase/home/homepage/components/CollectionSection/CollectionSection.tsx
index 06e6a71e69f..bb7bf30c915 100644
--- a/frontend/src/metabase/home/homepage/components/CollectionSection/CollectionSection.jsx
+++ b/frontend/src/metabase/home/homepage/components/CollectionSection/CollectionSection.tsx
@@ -1,26 +1,26 @@
 import React from "react";
-import PropTypes from "prop-types";
 import { t } from "ttag";
-import * as Urls from "metabase/lib/urls";
-import { ROOT_COLLECTION } from "metabase/entities/collections";
 import CollectionList from "metabase/components/CollectionList";
+import { ROOT_COLLECTION } from "metabase/entities/collections";
+import * as Urls from "metabase/lib/urls";
+import { Collection, User } from "../../types";
 import Section, { SectionHeader, SectionTitle } from "../Section";
 import {
+  CollectionContent,
   CollectionLink,
   CollectionLinkIcon,
   CollectionLinkText,
   EmptyStateImage,
   EmptyStateRoot,
   EmptyStateTitle,
-  CollectionContent,
 } from "./CollectionSection.styled";
 
-const propTypes = {
-  user: PropTypes.object.isRequired,
-  collections: PropTypes.array.isRequired,
-};
+interface Props {
+  user: User;
+  collections: Collection[];
+}
 
-const CollectionSection = ({ user, collections }) => {
+const CollectionSection = ({ user, collections }: Props) => {
   const showList = collections.some(c => c.id !== user.personal_collection_id);
   const collectionUrl = Urls.collection(ROOT_COLLECTION);
 
@@ -47,13 +47,11 @@ const CollectionSection = ({ user, collections }) => {
   );
 };
 
-CollectionSection.propTypes = propTypes;
+interface EmptyStateProps {
+  user: User;
+}
 
-const emptyStatePropTypes = {
-  user: PropTypes.object.isRequired,
-};
-
-const EmptyState = ({ user }) => {
+const EmptyState = ({ user }: EmptyStateProps) => {
   return (
     <EmptyStateRoot>
       <EmptyStateImage
@@ -69,6 +67,4 @@ const EmptyState = ({ user }) => {
   );
 };
 
-EmptyState.propTypes = emptyStatePropTypes;
-
 export default CollectionSection;
diff --git a/frontend/src/metabase/home/homepage/components/CollectionSection/CollectionSection.unit.spec.js b/frontend/src/metabase/home/homepage/components/CollectionSection/CollectionSection.unit.spec.tsx
similarity index 81%
rename from frontend/src/metabase/home/homepage/components/CollectionSection/CollectionSection.unit.spec.js
rename to frontend/src/metabase/home/homepage/components/CollectionSection/CollectionSection.unit.spec.tsx
index 1e878b7e8b1..f9df43b7b25 100644
--- a/frontend/src/metabase/home/homepage/components/CollectionSection/CollectionSection.unit.spec.js
+++ b/frontend/src/metabase/home/homepage/components/CollectionSection/CollectionSection.unit.spec.tsx
@@ -1,5 +1,6 @@
 import React from "react";
 import { render, screen } from "@testing-library/react";
+import { Collection, User } from "../../types";
 import CollectionSection from "./CollectionSection";
 
 const CollectionListMock = () => <div>CollectionList</div>;
@@ -35,12 +36,14 @@ describe("CollectionSection", () => {
   });
 });
 
-const getUser = ({
-  is_superuser = false,
-  personal_collection_id = "personal",
-} = {}) => ({
-  is_superuser,
-  personal_collection_id,
+const getUser = (opts?: Partial<User>): User => ({
+  first_name: "John",
+  is_superuser: false,
+  personal_collection_id: "personal",
+  ...opts,
 });
 
-const getCollection = ({ id = "root" } = {}) => ({ id });
+const getCollection = (opts?: Partial<Collection>): Collection => ({
+  id: "root",
+  ...opts,
+});
diff --git a/frontend/src/metabase/home/homepage/components/CollectionSection/index.js b/frontend/src/metabase/home/homepage/components/CollectionSection/index.ts
similarity index 100%
rename from frontend/src/metabase/home/homepage/components/CollectionSection/index.js
rename to frontend/src/metabase/home/homepage/components/CollectionSection/index.ts
diff --git a/frontend/src/metabase/home/homepage/components/DatabaseSection/DatabaseSection.styled.jsx b/frontend/src/metabase/home/homepage/components/DatabaseSection/DatabaseSection.styled.tsx
similarity index 100%
rename from frontend/src/metabase/home/homepage/components/DatabaseSection/DatabaseSection.styled.jsx
rename to frontend/src/metabase/home/homepage/components/DatabaseSection/DatabaseSection.styled.tsx
index e5d6cbc3f34..09fc3ca7026 100644
--- a/frontend/src/metabase/home/homepage/components/DatabaseSection/DatabaseSection.styled.jsx
+++ b/frontend/src/metabase/home/homepage/components/DatabaseSection/DatabaseSection.styled.tsx
@@ -1,7 +1,7 @@
-import styled from "styled-components";
 import { Link } from "react-router";
-import { color } from "metabase/lib/colors";
+import styled from "styled-components";
 import Icon from "metabase/components/Icon";
+import { color } from "metabase/lib/colors";
 import {
   breakpointMinMedium,
   breakpointMinSmall,
diff --git a/frontend/src/metabase/home/homepage/components/DatabaseSection/DatabaseSection.jsx b/frontend/src/metabase/home/homepage/components/DatabaseSection/DatabaseSection.tsx
similarity index 79%
rename from frontend/src/metabase/home/homepage/components/DatabaseSection/DatabaseSection.jsx
rename to frontend/src/metabase/home/homepage/components/DatabaseSection/DatabaseSection.tsx
index b9ee831ba9f..149192b953f 100644
--- a/frontend/src/metabase/home/homepage/components/DatabaseSection/DatabaseSection.jsx
+++ b/frontend/src/metabase/home/homepage/components/DatabaseSection/DatabaseSection.tsx
@@ -1,10 +1,10 @@
-import React from "react";
-import PropTypes from "prop-types";
+import React, { ReactNode } from "react";
 import { t } from "ttag";
-import * as Urls from "metabase/lib/urls";
 import Button from "metabase/components/Button";
-import Tooltip from "metabase/components/Tooltip";
 import ModalWithTrigger from "metabase/components/ModalWithTrigger";
+import Tooltip from "metabase/components/Tooltip";
+import * as Urls from "metabase/lib/urls";
+import { Database, User } from "../../types";
 import Section, {
   SectionCloseIcon,
   SectionHeader,
@@ -19,14 +19,14 @@ import {
   ListRoot,
 } from "./DatabaseSection.styled";
 
-const propTypes = {
-  user: PropTypes.object.isRequired,
-  databases: PropTypes.array.isRequired,
-  showData: PropTypes.bool,
-  onHideData: PropTypes.func,
-};
+interface Props {
+  user: User;
+  databases: Database[];
+  showData?: boolean;
+  onHideData?: () => void;
+}
 
-const DatabaseSection = ({ user, databases, showData, onHideData }) => {
+const DatabaseSection = ({ user, databases, showData, onHideData }: Props) => {
   const hasAddLink = user.is_superuser;
   const hasUserDatabase = databases.some(d => !d.is_sample);
 
@@ -39,11 +39,11 @@ const DatabaseSection = ({ user, databases, showData, onHideData }) => {
       <SectionHeader>
         <SectionTitle>{t`Our data`}</SectionTitle>
         {hasAddLink && (
-          <SectionRemoveModal onSubmit={onHideData}>
+          <HideSectionModal onSubmit={onHideData}>
             <Tooltip tooltip={t`Hide this section`}>
               <SectionCloseIcon name="close" />
             </Tooltip>
-          </SectionRemoveModal>
+          </HideSectionModal>
         )}
         {hasAddLink && hasUserDatabase && (
           <ActionLink to={Urls.newDatabase()}>{t`Add a database`}</ActionLink>
@@ -70,14 +70,12 @@ const DatabaseSection = ({ user, databases, showData, onHideData }) => {
   );
 };
 
-DatabaseSection.propTypes = propTypes;
-
-const modalPropTypes = {
-  children: PropTypes.node,
-  onSubmit: PropTypes.func,
-};
+interface HideSectionModalProps {
+  children?: ReactNode;
+  onSubmit?: () => void;
+}
 
-const SectionRemoveModal = ({ children, onSubmit }) => {
+const HideSectionModal = ({ children, onSubmit }: HideSectionModalProps) => {
   return (
     <ModalWithTrigger
       title={t`Remove this section?`}
@@ -91,6 +89,4 @@ const SectionRemoveModal = ({ children, onSubmit }) => {
   );
 };
 
-SectionRemoveModal.propTypes = modalPropTypes;
-
 export default DatabaseSection;
diff --git a/frontend/src/metabase/home/homepage/components/DatabaseSection/DatabaseSection.unit.spec.js b/frontend/src/metabase/home/homepage/components/DatabaseSection/DatabaseSection.unit.spec.tsx
similarity index 84%
rename from frontend/src/metabase/home/homepage/components/DatabaseSection/DatabaseSection.unit.spec.js
rename to frontend/src/metabase/home/homepage/components/DatabaseSection/DatabaseSection.unit.spec.tsx
index d8cdb9e5ce3..5dbe44558a5 100644
--- a/frontend/src/metabase/home/homepage/components/DatabaseSection/DatabaseSection.unit.spec.js
+++ b/frontend/src/metabase/home/homepage/components/DatabaseSection/DatabaseSection.unit.spec.tsx
@@ -1,6 +1,7 @@
 import React from "react";
 import { render, screen } from "@testing-library/react";
 import userEvent from "@testing-library/user-event";
+import { Database, User } from "../../types";
 import DatabaseSection from "./DatabaseSection";
 
 describe("DatabaseSection", () => {
@@ -41,18 +42,16 @@ describe("DatabaseSection", () => {
 
   it("should not be visible for regular users when there are no databases", () => {
     const user = getUser();
-    const databases = [];
 
-    render(<DatabaseSection user={user} databases={databases} showData />);
+    render(<DatabaseSection user={user} databases={[]} showData />);
 
     expect(screen.queryByText("Our data")).not.toBeInTheDocument();
   });
 
   it("should be visible for admin users when there are no databases", () => {
     const user = getUser({ is_superuser: true });
-    const databases = [];
 
-    render(<DatabaseSection user={user} databases={databases} showData />);
+    render(<DatabaseSection user={user} databases={[]} showData />);
 
     expect(screen.getByText("Our data")).toBeInTheDocument();
     expect(screen.getByText("Add a database")).toBeInTheDocument();
@@ -60,13 +59,12 @@ describe("DatabaseSection", () => {
 
   it("should allow admins to hide the section", () => {
     const user = getUser({ is_superuser: true });
-    const databases = [];
     const onHideData = jest.fn();
 
     render(
       <DatabaseSection
         user={user}
-        databases={databases}
+        databases={[]}
         showData
         onHideData={onHideData}
       />,
@@ -80,13 +78,12 @@ describe("DatabaseSection", () => {
 
   it("should not allow regular users to hide the section", () => {
     const user = getUser({ is_superuser: false });
-    const databases = [];
     const onHideData = jest.fn();
 
     render(
       <DatabaseSection
         user={user}
-        databases={databases}
+        databases={[]}
         showData
         onHideData={onHideData}
       />,
@@ -96,6 +93,16 @@ describe("DatabaseSection", () => {
   });
 });
 
-const getUser = ({ is_superuser = false } = {}) => ({ is_superuser });
+const getUser = (opts?: Partial<User>): User => ({
+  first_name: "John",
+  is_superuser: false,
+  personal_collection_id: "personal",
+  ...opts,
+});
 
-const getDatabase = ({ id = 1, name } = {}) => ({ id, name });
+const getDatabase = (opts?: Partial<Database>): Database => ({
+  id: 1,
+  name: "Our database",
+  is_sample: false,
+  ...opts,
+});
diff --git a/frontend/src/metabase/home/homepage/components/DatabaseSection/index.js b/frontend/src/metabase/home/homepage/components/DatabaseSection/index.ts
similarity index 100%
rename from frontend/src/metabase/home/homepage/components/DatabaseSection/index.js
rename to frontend/src/metabase/home/homepage/components/DatabaseSection/index.ts
diff --git a/frontend/src/metabase/home/homepage/components/GreetingSection/GreetingSection.styled.jsx b/frontend/src/metabase/home/homepage/components/GreetingSection/GreetingSection.styled.tsx
similarity index 100%
rename from frontend/src/metabase/home/homepage/components/GreetingSection/GreetingSection.styled.jsx
rename to frontend/src/metabase/home/homepage/components/GreetingSection/GreetingSection.styled.tsx
diff --git a/frontend/src/metabase/home/homepage/components/GreetingSection/GreetingSection.jsx b/frontend/src/metabase/home/homepage/components/GreetingSection/GreetingSection.tsx
similarity index 79%
rename from frontend/src/metabase/home/homepage/components/GreetingSection/GreetingSection.jsx
rename to frontend/src/metabase/home/homepage/components/GreetingSection/GreetingSection.tsx
index a27bfa21ef6..1ad178b441d 100644
--- a/frontend/src/metabase/home/homepage/components/GreetingSection/GreetingSection.jsx
+++ b/frontend/src/metabase/home/homepage/components/GreetingSection/GreetingSection.tsx
@@ -1,17 +1,17 @@
 import React, { useMemo } from "react";
-import PropTypes from "prop-types";
 import { t } from "ttag";
-import Greeting from "metabase/lib/greeting";
 import MetabotLogo from "metabase/components/MetabotLogo";
 import Tooltip from "metabase/components/Tooltip";
+import Greeting from "metabase/lib/greeting";
+import { User } from "../../types";
 import Section from "../Section";
 import { GreetingContent, GreetingTitle } from "./GreetingSection.styled";
 
-const propTypes = {
-  user: PropTypes.object.isRequired,
-};
+interface Props {
+  user: User;
+}
 
-const GreetingSection = ({ user: { first_name } }) => {
+const GreetingSection = ({ user: { first_name } }: Props) => {
   const greeting = useMemo(() => Greeting.sayHello(first_name), [first_name]);
 
   return (
@@ -29,6 +29,4 @@ const GreetingSection = ({ user: { first_name } }) => {
   );
 };
 
-GreetingSection.propTypes = propTypes;
-
 export default GreetingSection;
diff --git a/frontend/src/metabase/home/homepage/components/GreetingSection/GreetingSection.unit.spec.js b/frontend/src/metabase/home/homepage/components/GreetingSection/GreetingSection.unit.spec.tsx
similarity index 67%
rename from frontend/src/metabase/home/homepage/components/GreetingSection/GreetingSection.unit.spec.js
rename to frontend/src/metabase/home/homepage/components/GreetingSection/GreetingSection.unit.spec.tsx
index 60990cfc86f..1e630be4352 100644
--- a/frontend/src/metabase/home/homepage/components/GreetingSection/GreetingSection.unit.spec.js
+++ b/frontend/src/metabase/home/homepage/components/GreetingSection/GreetingSection.unit.spec.tsx
@@ -1,5 +1,6 @@
 import React from "react";
 import { render, screen } from "@testing-library/react";
+import { User } from "../../types";
 import GreetingSection from "./GreetingSection";
 
 describe("GreetingSection", () => {
@@ -12,4 +13,9 @@ describe("GreetingSection", () => {
   });
 });
 
-const getUser = ({ first_name } = {}) => ({ first_name });
+const getUser = (opts?: Partial<User>): User => ({
+  first_name: "John",
+  is_superuser: false,
+  personal_collection_id: "personal",
+  ...opts,
+});
diff --git a/frontend/src/metabase/home/homepage/components/GreetingSection/index.js b/frontend/src/metabase/home/homepage/components/GreetingSection/index.ts
similarity index 100%
rename from frontend/src/metabase/home/homepage/components/GreetingSection/index.js
rename to frontend/src/metabase/home/homepage/components/GreetingSection/index.ts
diff --git a/frontend/src/metabase/home/homepage/components/Homepage/Homepage.styled.jsx b/frontend/src/metabase/home/homepage/components/Homepage/Homepage.styled.tsx
similarity index 100%
rename from frontend/src/metabase/home/homepage/components/Homepage/Homepage.styled.jsx
rename to frontend/src/metabase/home/homepage/components/Homepage/Homepage.styled.tsx
diff --git a/frontend/src/metabase/home/homepage/components/Homepage/Homepage.jsx b/frontend/src/metabase/home/homepage/components/Homepage/Homepage.tsx
similarity index 75%
rename from frontend/src/metabase/home/homepage/components/Homepage/Homepage.jsx
rename to frontend/src/metabase/home/homepage/components/Homepage/Homepage.tsx
index 7ac38598adf..b0ee92351f3 100644
--- a/frontend/src/metabase/home/homepage/components/Homepage/Homepage.jsx
+++ b/frontend/src/metabase/home/homepage/components/Homepage/Homepage.tsx
@@ -1,25 +1,31 @@
 import React, { Fragment } from "react";
-import PropTypes from "prop-types";
-import GreetingSection from "../GreetingSection";
+import {
+  Collection,
+  Dashboard,
+  Database,
+  DatabaseCandidate,
+  User,
+} from "../../types";
 import CollectionSection from "../CollectionSection";
 import DatabaseSection from "../DatabaseSection";
+import GreetingSection from "../GreetingSection";
 import StartSection from "../StartSection";
 import XraySection from "../XraySection";
 import { LandingRoot } from "./Homepage.styled";
 
-const propTypes = {
-  user: PropTypes.object.isRequired,
-  databases: PropTypes.array,
-  collections: PropTypes.array,
-  dashboards: PropTypes.array,
-  databaseCandidates: PropTypes.array,
-  showData: PropTypes.bool,
-  showXrays: PropTypes.bool,
-  showPinMessage: PropTypes.bool,
-  onHideData: PropTypes.func,
-  onHideXrays: PropTypes.func,
-  onHidePinMessage: PropTypes.func,
-};
+interface Props {
+  user: User;
+  databases?: Database[];
+  collections?: Collection[];
+  dashboards?: Dashboard[];
+  databaseCandidates?: DatabaseCandidate[];
+  showData?: boolean;
+  showXrays?: boolean;
+  showPinMessage?: boolean;
+  onHideData?: () => void;
+  onHideXrays?: () => void;
+  onHidePinMessage?: () => void;
+}
 
 const Homepage = ({
   user,
@@ -33,7 +39,7 @@ const Homepage = ({
   onHideData,
   onHideXrays,
   onHidePinMessage,
-}) => {
+}: Props) => {
   return (
     <LandingRoot>
       <GreetingSection user={user} />
@@ -66,6 +72,4 @@ const Homepage = ({
   );
 };
 
-Homepage.propTypes = propTypes;
-
 export default Homepage;
diff --git a/frontend/src/metabase/home/homepage/components/Homepage/index.js b/frontend/src/metabase/home/homepage/components/Homepage/index.ts
similarity index 100%
rename from frontend/src/metabase/home/homepage/components/Homepage/index.js
rename to frontend/src/metabase/home/homepage/components/Homepage/index.ts
diff --git a/frontend/src/metabase/home/homepage/components/Section/Section.styled.jsx b/frontend/src/metabase/home/homepage/components/Section/Section.styled.tsx
similarity index 100%
rename from frontend/src/metabase/home/homepage/components/Section/Section.styled.jsx
rename to frontend/src/metabase/home/homepage/components/Section/Section.styled.tsx
index 3784f71d82a..c59410ebc77 100644
--- a/frontend/src/metabase/home/homepage/components/Section/Section.styled.jsx
+++ b/frontend/src/metabase/home/homepage/components/Section/Section.styled.tsx
@@ -1,6 +1,6 @@
 import styled from "styled-components";
-import { color } from "metabase/lib/colors";
 import Icon from "metabase/components/Icon";
+import { color } from "metabase/lib/colors";
 
 export const SectionHeader = styled.div`
   display: flex;
diff --git a/frontend/src/metabase/home/homepage/components/Section/index.js b/frontend/src/metabase/home/homepage/components/Section/index.ts
similarity index 100%
rename from frontend/src/metabase/home/homepage/components/Section/index.js
rename to frontend/src/metabase/home/homepage/components/Section/index.ts
diff --git a/frontend/src/metabase/home/homepage/components/StartSection/StartSection.styled.jsx b/frontend/src/metabase/home/homepage/components/StartSection/StartSection.styled.tsx
similarity index 95%
rename from frontend/src/metabase/home/homepage/components/StartSection/StartSection.styled.jsx
rename to frontend/src/metabase/home/homepage/components/StartSection/StartSection.styled.tsx
index 0f2965e5800..e84f53a891d 100644
--- a/frontend/src/metabase/home/homepage/components/StartSection/StartSection.styled.jsx
+++ b/frontend/src/metabase/home/homepage/components/StartSection/StartSection.styled.tsx
@@ -1,13 +1,17 @@
-import styled from "styled-components";
 import { Link } from "react-router";
-import { color } from "metabase/lib/colors";
+import styled from "styled-components";
 import Icon from "metabase/components/Icon";
+import { color } from "metabase/lib/colors";
 import {
   breakpointMinMedium,
   breakpointMinSmall,
 } from "metabase/styled-components/theme";
 
-export const ListRoot = styled.div`
+interface ListRootProps {
+  hasMargin?: boolean;
+}
+
+export const ListRoot = styled.div<ListRootProps>`
   display: grid;
   grid-template-columns: repeat(1, 1fr);
   gap: 1rem;
diff --git a/frontend/src/metabase/home/homepage/components/StartSection/StartSection.jsx b/frontend/src/metabase/home/homepage/components/StartSection/StartSection.tsx
similarity index 84%
rename from frontend/src/metabase/home/homepage/components/StartSection/StartSection.jsx
rename to frontend/src/metabase/home/homepage/components/StartSection/StartSection.tsx
index f6ff201b2b8..2d8deb9f80e 100644
--- a/frontend/src/metabase/home/homepage/components/StartSection/StartSection.jsx
+++ b/frontend/src/metabase/home/homepage/components/StartSection/StartSection.tsx
@@ -1,11 +1,11 @@
 import React from "react";
-import PropTypes from "prop-types";
 import { jt, t } from "ttag";
+import ExternalLink from "metabase/components/ExternalLink";
+import Link from "metabase/components/Link";
+import { ROOT_COLLECTION } from "metabase/entities/collections";
 import Settings from "metabase/lib/settings";
 import * as Urls from "metabase/lib/urls";
-import { ROOT_COLLECTION } from "metabase/entities/collections";
-import Link from "metabase/components/Link";
-import ExternalLink from "metabase/components/ExternalLink";
+import { Dashboard, Database, User } from "../../types";
 import Section, { SectionHeader, SectionTitle } from "../Section";
 import {
   BannerCloseIcon,
@@ -21,13 +21,13 @@ import {
   ListRoot,
 } from "./StartSection.styled";
 
-const propTypes = {
-  user: PropTypes.object.isRequired,
-  databases: PropTypes.array.isRequired,
-  dashboards: PropTypes.array.isRequired,
-  showPinMessage: PropTypes.bool,
-  onHidePinMessage: PropTypes.func,
-};
+interface Props {
+  user: User;
+  databases: Database[];
+  dashboards: Dashboard[];
+  showPinMessage?: boolean;
+  onHidePinMessage?: () => void;
+}
 
 const StartSection = ({
   user,
@@ -35,7 +35,7 @@ const StartSection = ({
   dashboards,
   showPinMessage,
   onHidePinMessage,
-}) => {
+}: Props) => {
   const showDatabaseBanner =
     user.is_superuser && !databases.some(d => !d.is_sample);
   const showDashboardBanner =
@@ -66,13 +66,11 @@ const StartSection = ({
   );
 };
 
-StartSection.propTypes = propTypes;
-
-const cardProps = {
-  dashboard: PropTypes.object,
-};
+interface DashboardCardProps {
+  dashboard: Dashboard;
+}
 
-const DashboardCard = ({ dashboard }) => {
+const DashboardCard = ({ dashboard }: DashboardCardProps) => {
   const dashboardUrl = Urls.dashboard(dashboard);
 
   return (
@@ -83,8 +81,6 @@ const DashboardCard = ({ dashboard }) => {
   );
 };
 
-DashboardCard.propTypes = cardProps;
-
 const DatabaseBanner = () => {
   const userUrl = Urls.newUser();
   const databaseUrl = Urls.newDatabase();
@@ -117,12 +113,12 @@ const DatabaseBanner = () => {
   );
 };
 
-const dashboardBannerProps = {
-  user: PropTypes.object.isRequired,
-  onHidePinMessage: PropTypes.func,
-};
+interface DashboardBannerProps {
+  user: User;
+  onHidePinMessage?: () => void;
+}
 
-const DashboardBanner = ({ user, onHidePinMessage }) => {
+const DashboardBanner = ({ user, onHidePinMessage }: DashboardBannerProps) => {
   const collectionUrl = Urls.collection(ROOT_COLLECTION);
 
   return (
@@ -145,6 +141,4 @@ const DashboardBanner = ({ user, onHidePinMessage }) => {
   );
 };
 
-DashboardBanner.propTypes = dashboardBannerProps;
-
 export default StartSection;
diff --git a/frontend/src/metabase/home/homepage/components/StartSection/StartSection.unit.spec.js b/frontend/src/metabase/home/homepage/components/StartSection/StartSection.unit.spec.tsx
similarity index 84%
rename from frontend/src/metabase/home/homepage/components/StartSection/StartSection.unit.spec.js
rename to frontend/src/metabase/home/homepage/components/StartSection/StartSection.unit.spec.tsx
index 091041e41ee..25c0a492e8c 100644
--- a/frontend/src/metabase/home/homepage/components/StartSection/StartSection.unit.spec.js
+++ b/frontend/src/metabase/home/homepage/components/StartSection/StartSection.unit.spec.tsx
@@ -1,6 +1,7 @@
 import React from "react";
 import { render, screen } from "@testing-library/react";
 import userEvent from "@testing-library/user-event";
+import { Dashboard, Database, User } from "../../types";
 import StartSection from "./StartSection";
 
 describe("StartSection", () => {
@@ -46,13 +47,12 @@ describe("StartSection", () => {
 
   it("should not show a banner for regular users when there are no user databases", () => {
     const user = getUser();
-    const databases = [];
     const dashboards = [getDashboard({ name: "Our dashboard" })];
 
     render(
       <StartSection
         user={user}
-        databases={databases}
+        databases={[]}
         dashboards={dashboards}
         showPinMessage={true}
       />,
@@ -66,14 +66,12 @@ describe("StartSection", () => {
 
   it("should show a banner for admins when there are no pinned dashboards", () => {
     const user = getUser({ is_superuser: true });
-    const databases = [];
-    const dashboards = [];
 
     render(
       <StartSection
         user={user}
-        databases={databases}
-        dashboards={dashboards}
+        databases={[]}
+        dashboards={[]}
         showPinMessage={true}
       />,
     );
@@ -85,14 +83,12 @@ describe("StartSection", () => {
 
   it("should show a banner for regular users when there are no pinned dashboards", () => {
     const user = getUser();
-    const databases = [];
-    const dashboards = [];
 
     render(
       <StartSection
         user={user}
-        databases={databases}
-        dashboards={dashboards}
+        databases={[]}
+        dashboards={[]}
         showPinMessage={true}
       />,
     );
@@ -104,14 +100,12 @@ describe("StartSection", () => {
 
   it("should not hide the section for admins when there is no content", () => {
     const user = getUser({ is_superuser: true });
-    const databases = [];
-    const dashboards = [];
 
     render(
       <StartSection
         user={user}
-        databases={databases}
-        dashboards={dashboards}
+        databases={[]}
+        dashboards={[]}
         showPinMessage={false}
       />,
     );
@@ -122,14 +116,12 @@ describe("StartSection", () => {
 
   it("should hide the section for regular users when there is no content", () => {
     const user = getUser();
-    const databases = [];
-    const dashboards = [];
 
     render(
       <StartSection
         user={user}
-        databases={databases}
-        dashboards={dashboards}
+        databases={[]}
+        dashboards={[]}
         showPinMessage={false}
       />,
     );
@@ -140,14 +132,13 @@ describe("StartSection", () => {
   it("should allow admins to hide the dashboard banner", () => {
     const user = getUser({ is_superuser: true });
     const databases = [getDatabase()];
-    const dashboards = [];
     const onHidePinMessage = jest.fn();
 
     render(
       <StartSection
         user={user}
         databases={databases}
-        dashboards={dashboards}
+        dashboards={[]}
         showPinMessage={true}
         onHidePinMessage={onHidePinMessage}
       />,
@@ -160,14 +151,13 @@ describe("StartSection", () => {
   it("should not allow regular users to hide the dashboard banner", () => {
     const user = getUser();
     const databases = [getDatabase()];
-    const dashboards = [];
     const onHidePinMessage = jest.fn();
 
     render(
       <StartSection
         user={user}
         databases={databases}
-        dashboards={dashboards}
+        dashboards={[]}
         showPinMessage={true}
         onHidePinMessage={onHidePinMessage}
       />,
@@ -177,8 +167,22 @@ describe("StartSection", () => {
   });
 });
 
-const getUser = ({ is_superuser = false } = {}) => ({ is_superuser });
+const getUser = (opts?: Partial<User>): User => ({
+  first_name: "John",
+  is_superuser: false,
+  personal_collection_id: "personal",
+  ...opts,
+});
 
-const getDatabase = ({ id = 1, is_sample = false } = {}) => ({ id, is_sample });
+const getDatabase = (opts?: Partial<Database>): Database => ({
+  id: 1,
+  name: "Our database",
+  is_sample: false,
+  ...opts,
+});
 
-const getDashboard = ({ id = 1, name } = {}) => ({ id, name });
+const getDashboard = (opts?: Partial<Dashboard>): Dashboard => ({
+  id: 1,
+  name: "Our dashboard",
+  ...opts,
+});
diff --git a/frontend/src/metabase/home/homepage/components/StartSection/index.js b/frontend/src/metabase/home/homepage/components/StartSection/index.ts
similarity index 100%
rename from frontend/src/metabase/home/homepage/components/StartSection/index.js
rename to frontend/src/metabase/home/homepage/components/StartSection/index.ts
diff --git a/frontend/src/metabase/home/homepage/components/XraySection/XraySection.styled.jsx b/frontend/src/metabase/home/homepage/components/XraySection/XraySection.styled.tsx
similarity index 100%
rename from frontend/src/metabase/home/homepage/components/XraySection/XraySection.styled.jsx
rename to frontend/src/metabase/home/homepage/components/XraySection/XraySection.styled.tsx
index 05d81942245..e1a4bab24fb 100644
--- a/frontend/src/metabase/home/homepage/components/XraySection/XraySection.styled.jsx
+++ b/frontend/src/metabase/home/homepage/components/XraySection/XraySection.styled.tsx
@@ -1,7 +1,7 @@
-import styled from "styled-components";
 import { Link } from "react-router";
-import { color } from "metabase/lib/colors";
+import styled from "styled-components";
 import Icon from "metabase/components/Icon";
+import { color } from "metabase/lib/colors";
 import {
   breakpointMinMedium,
   breakpointMinSmall,
diff --git a/frontend/src/metabase/home/homepage/components/XraySection/XraySection.jsx b/frontend/src/metabase/home/homepage/components/XraySection/XraySection.tsx
similarity index 72%
rename from frontend/src/metabase/home/homepage/components/XraySection/XraySection.jsx
rename to frontend/src/metabase/home/homepage/components/XraySection/XraySection.tsx
index e22fc0cd66e..7069f5b3815 100644
--- a/frontend/src/metabase/home/homepage/components/XraySection/XraySection.jsx
+++ b/frontend/src/metabase/home/homepage/components/XraySection/XraySection.tsx
@@ -1,12 +1,17 @@
-import React from "react";
-import PropTypes from "prop-types";
+import React, { ReactNode } from "react";
 import { t } from "ttag";
 import Button from "metabase/components/Button";
-import Tooltip from "metabase/components/Tooltip";
 import ModalWithTrigger from "metabase/components/ModalWithTrigger";
+import Tooltip from "metabase/components/Tooltip";
+import {
+  Dashboard,
+  DatabaseCandidate,
+  TableCandidate,
+  User,
+} from "../../types";
 import Section, {
-  SectionHeader,
   SectionCloseIcon,
+  SectionHeader,
   SectionTitle,
 } from "../Section";
 import {
@@ -17,13 +22,13 @@ import {
   ListRoot,
 } from "./XraySection.styled";
 
-const propTypes = {
-  user: PropTypes.object.isRequired,
-  dashboards: PropTypes.array,
-  databaseCandidates: PropTypes.array,
-  showXrays: PropTypes.bool,
-  onHideXrays: PropTypes.func,
-};
+interface Props {
+  user: User;
+  dashboards: Dashboard[];
+  databaseCandidates?: DatabaseCandidate[];
+  showXrays?: boolean;
+  onHideXrays?: () => void;
+}
 
 const XraySection = ({
   user,
@@ -31,7 +36,7 @@ const XraySection = ({
   databaseCandidates = [],
   showXrays,
   onHideXrays,
-}) => {
+}: Props) => {
   const options = databaseCandidates.flatMap(database => database.tables);
 
   if (!showXrays || dashboards.length || !options.length) {
@@ -43,11 +48,11 @@ const XraySection = ({
       <SectionHeader>
         <SectionTitle>{t`Try these x-rays based on your data`}</SectionTitle>
         {user.is_superuser && (
-          <SectionRemoveModal onSubmit={onHideXrays}>
+          <HideSectionModal onSubmit={onHideXrays}>
             <Tooltip tooltip={t`Remove these suggestions`}>
               <SectionCloseIcon name="close" />
             </Tooltip>
-          </SectionRemoveModal>
+          </HideSectionModal>
         )}
       </SectionHeader>
       <ListRoot>
@@ -59,13 +64,11 @@ const XraySection = ({
   );
 };
 
-XraySection.propTypes = propTypes;
+interface XrayCardProps {
+  option: TableCandidate;
+}
 
-const cardPropTypes = {
-  option: PropTypes.object.isRequired,
-};
-
-const XrayCard = ({ option }) => {
+const XrayCard = ({ option }: XrayCardProps) => {
   return (
     <CardRoot to={option.url}>
       <CardIconContainer>
@@ -78,14 +81,12 @@ const XrayCard = ({ option }) => {
   );
 };
 
-XrayCard.propTypes = cardPropTypes;
+interface HideSectionModalProps {
+  children?: ReactNode;
+  onSubmit?: () => void;
+}
 
-const modalPropTypes = {
-  children: PropTypes.node,
-  onSubmit: PropTypes.func,
-};
-
-const SectionRemoveModal = ({ children, onSubmit }) => {
+const HideSectionModal = ({ children, onSubmit }: HideSectionModalProps) => {
   return (
     <ModalWithTrigger
       title={t`Remove these suggestions?`}
@@ -99,6 +100,4 @@ const SectionRemoveModal = ({ children, onSubmit }) => {
   );
 };
 
-SectionRemoveModal.propTypes = modalPropTypes;
-
 export default XraySection;
diff --git a/frontend/src/metabase/home/homepage/components/XraySection/XraySection.unit.spec.js b/frontend/src/metabase/home/homepage/components/XraySection/XraySection.unit.spec.tsx
similarity index 82%
rename from frontend/src/metabase/home/homepage/components/XraySection/XraySection.unit.spec.js
rename to frontend/src/metabase/home/homepage/components/XraySection/XraySection.unit.spec.tsx
index b1819d0625e..00d376f0e41 100644
--- a/frontend/src/metabase/home/homepage/components/XraySection/XraySection.unit.spec.js
+++ b/frontend/src/metabase/home/homepage/components/XraySection/XraySection.unit.spec.tsx
@@ -1,6 +1,12 @@
 import React from "react";
 import { render, screen } from "@testing-library/react";
 import userEvent from "@testing-library/user-event";
+import {
+  Dashboard,
+  DatabaseCandidate,
+  TableCandidate,
+  User,
+} from "../../types";
 import XraySection from "./XraySection";
 
 describe("XraySection", () => {
@@ -99,13 +105,12 @@ describe("XraySection", () => {
 
   it("should not be visible when there are no table candidates", () => {
     const user = getUser();
-    const dashboards = [];
     const databaseCandidates = [getDatabaseCandidate()];
 
     render(
       <XraySection
         user={user}
-        dashboards={dashboards}
+        dashboards={[]}
         databaseCandidates={databaseCandidates}
         showXrays
       />,
@@ -115,10 +120,28 @@ describe("XraySection", () => {
   });
 });
 
-const getUser = ({ is_superuser = false } = {}) => ({ is_superuser });
+const getUser = (opts?: Partial<User>): User => ({
+  first_name: "John",
+  is_superuser: false,
+  personal_collection_id: "personal",
+  ...opts,
+});
 
-const getDashboard = ({ id = 1 } = {}) => ({ id });
+const getDashboard = (opts?: Partial<Dashboard>): Dashboard => ({
+  id: 1,
+  name: "Our dashboard",
+  ...opts,
+});
 
-const getTableCandidate = ({ title, url = "/" } = {}) => ({ title, url });
+const getTableCandidate = (opts?: Partial<TableCandidate>): TableCandidate => ({
+  title: "Our table",
+  url: "/auto",
+  ...opts,
+});
 
-const getDatabaseCandidate = ({ tables = [] } = {}) => ({ tables });
+const getDatabaseCandidate = (
+  opts?: Partial<DatabaseCandidate>,
+): DatabaseCandidate => ({
+  tables: [],
+  ...opts,
+});
diff --git a/frontend/src/metabase/home/homepage/components/XraySection/index.js b/frontend/src/metabase/home/homepage/components/XraySection/index.ts
similarity index 100%
rename from frontend/src/metabase/home/homepage/components/XraySection/index.js
rename to frontend/src/metabase/home/homepage/components/XraySection/index.ts
diff --git a/frontend/src/metabase/home/homepage/containers/HomepageApp/HomepageApp.jsx b/frontend/src/metabase/home/homepage/containers/HomepageApp/HomepageApp.tsx
similarity index 92%
rename from frontend/src/metabase/home/homepage/containers/HomepageApp/HomepageApp.jsx
rename to frontend/src/metabase/home/homepage/containers/HomepageApp/HomepageApp.tsx
index b53226a0b32..d664f3eaf11 100644
--- a/frontend/src/metabase/home/homepage/containers/HomepageApp/HomepageApp.jsx
+++ b/frontend/src/metabase/home/homepage/containers/HomepageApp/HomepageApp.tsx
@@ -8,6 +8,7 @@ import { getUser } from "metabase/selectors/user";
 import Homepage from "../../components/Homepage";
 import { hideData, hidePinMessage, hideXrays } from "../../actions";
 import { getShowData, getShowPinMessage, getShowXrays } from "../../selectors";
+import { Database } from "../../types";
 
 const databasesProps = {
   loadingAndErrorWrapper: false,
@@ -38,7 +39,7 @@ const dashboardsProps = {
 };
 
 const databaseCandidatesProps = {
-  query: (state, { databases = [] }) => {
+  query: (state: any, { databases = [] }: { databases: Database[] }) => {
     const [sampleDatabases, userDatabases] = _.partition(
       databases,
       d => d.is_sample,
@@ -53,7 +54,7 @@ const databaseCandidatesProps = {
   loadingAndErrorWrapper: false,
 };
 
-const mapStateToProps = state => ({
+const mapStateToProps = (state: any) => ({
   user: getUser(state),
   showData: getShowData(state),
   showXrays: getShowXrays(state),
diff --git a/frontend/src/metabase/home/homepage/containers/HomepageApp/index.js b/frontend/src/metabase/home/homepage/containers/HomepageApp/index.ts
similarity index 100%
rename from frontend/src/metabase/home/homepage/containers/HomepageApp/index.js
rename to frontend/src/metabase/home/homepage/containers/HomepageApp/index.ts
diff --git a/frontend/src/metabase/home/homepage/selectors.js b/frontend/src/metabase/home/homepage/selectors.ts
similarity index 88%
rename from frontend/src/metabase/home/homepage/selectors.js
rename to frontend/src/metabase/home/homepage/selectors.ts
index 5e1b95b72e4..f59450cbd03 100644
--- a/frontend/src/metabase/home/homepage/selectors.js
+++ b/frontend/src/metabase/home/homepage/selectors.ts
@@ -1,6 +1,6 @@
 import { createSelector } from "reselect";
 
-export const getSettings = createSelector(
+export const getSettings = createSelector<any, any, any>(
   state => state.settings,
   settings => settings.values,
 );
diff --git a/frontend/src/metabase/home/homepage/types.ts b/frontend/src/metabase/home/homepage/types.ts
new file mode 100644
index 00000000000..0a850e07571
--- /dev/null
+++ b/frontend/src/metabase/home/homepage/types.ts
@@ -0,0 +1,29 @@
+export interface User {
+  first_name: string;
+  is_superuser: boolean;
+  personal_collection_id: string;
+}
+
+export interface Database {
+  id: number;
+  name: string;
+  is_sample: boolean;
+}
+
+export interface Collection {
+  id: string;
+}
+
+export interface Dashboard {
+  id: number;
+  name: string;
+}
+
+export interface DatabaseCandidate {
+  tables: TableCandidate[];
+}
+
+export interface TableCandidate {
+  title: string;
+  url: string;
+}
diff --git a/jest.unit.conf.json b/jest.unit.conf.json
index c01a1dcc283..0a457db8a35 100644
--- a/jest.unit.conf.json
+++ b/jest.unit.conf.json
@@ -4,8 +4,8 @@
     "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/frontend/test/__mocks__/fileMock.js",
     "^promise-loader\\?global\\!metabase\\/lib\\/ga-metadata$": "<rootDir>/frontend/src/metabase/lib/ga-metadata.js"
   },
-  "testPathIgnorePatterns": ["<rootDir>/frontend/test/.*/.*.tz.unit.spec.{js,ts}"],
-  "testMatch": ["<rootDir>/**/*.unit.spec.js", "<rootDir>/**/*.unit.spec.{js,ts}"],
+  "testPathIgnorePatterns": ["<rootDir>/frontend/test/.*/.*.tz.unit.spec.{js,jsx,ts,tsx}"],
+  "testMatch": ["<rootDir>/**/*.unit.spec.js", "<rootDir>/**/*.unit.spec.{js,jsx,ts,tsx}"],
   "modulePaths": [
     "<rootDir>/frontend/test",
     "<rootDir>/frontend/src",
-- 
GitLab