diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..e3667cd38a13d16dd8056c86b3332cdf20e942e0
--- /dev/null
+++ b/e2e/tsconfig.json
@@ -0,0 +1,11 @@
+{
+  "extends": "../tsconfig.base.json",
+  "compilerOptions": {
+    "types": ["cypress", "@testing-library/cypress"],
+    "paths": {
+      "*": ["../frontend/src/*"],
+      "e2e/*": ["./*"]
+    }
+  },
+  "include": ["**/*"]
+}
diff --git a/jsconfig.json b/jsconfig.json
deleted file mode 100644
index 16599a286858eac14e62c8257817676bfffb4e49..0000000000000000000000000000000000000000
--- a/jsconfig.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "include": [
-    "frontend/src/**/*",
-    "enterprise/frontend/src/**/*",
-    "frontend/test/**/*",
-    "enterprise/frontend/test/**/*"
-  ],
-  "compilerOptions": {
-    "baseUrl": ".",
-    "paths": {
-      "*": [
-        "./frontend/src/*",
-        "./frontend/test/*",
-        "./enterprise/frontend/src/*",
-        "./enterprise/frontend/test/*"
-      ]
-    },
-    "experimentalDecorators": true,
-    "strictBindCallApply": true
-  }
-}
diff --git a/tsconfig.base.json b/tsconfig.base.json
new file mode 100644
index 0000000000000000000000000000000000000000..b8fe21cf54969cbb366f6dbe661e8a9513fb02bb
--- /dev/null
+++ b/tsconfig.base.json
@@ -0,0 +1,18 @@
+{
+  "compilerOptions": {
+    "jsx": "react",
+    "target": "esnext",
+    "allowSyntheticDefaultImports": true,
+    "module": "commonjs",
+    "isolatedModules": true,
+    "strict": true,
+    "moduleResolution": "node",
+    "skipLibCheck": true,
+    "allowJs": true,
+    "esModuleInterop": true,
+    "experimentalDecorators": true,
+    "forceConsistentCasingInFileNames": true,
+    "resolveJsonModule": true,
+    "lib": ["dom", "dom.iterable", "esnext"]
+  }
+}
diff --git a/tsconfig.json b/tsconfig.json
index a1901b6d0a7e1e5ad9fcfb27347fe462a2ca4429..211182262747bd40759146aee545d3f46b042463 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,38 +1,27 @@
 {
+  "extends": "./tsconfig.base.json",
   "compilerOptions": {
-    "jsx": "react",
-    "target": "esnext",
-    "lib": ["dom", "dom.iterable", "esnext"],
-    "allowSyntheticDefaultImports": true,
-    "module": "commonjs",
-    "isolatedModules": true,
-    "strict": true,
-    "moduleResolution": "node",
-    "skipLibCheck": true,
     "paths": {
       "*": [
         "./frontend/src/*",
         "./frontend/test/*",
         "./enterprise/frontend/src/*",
         "./enterprise/frontend/test/*"
-      ],
-      "e2e/*": ["./e2e/*"]
-    },
-    "allowJs": true,
-    "esModuleInterop": true,
-    "experimentalDecorators": true,
-    "forceConsistentCasingInFileNames": true,
-    "resolveJsonModule": true
+      ]
+    }
   },
   "include": [
-    "frontend/src/**/*.ts",
-    "frontend/src/**/*.tsx",
-    "enterprise/frontend/src/**/*.ts",
-    "enterprise/frontend/src/**/*.tsx",
-    "frontend/test/**/*.ts",
-    "frontend/test/**/*.tsx",
-    "enterprise/frontend/test/**/*.ts",
-    "enterprise/frontend/test/**/*.tsx"
+    "frontend/src/**/*",
+    "enterprise/frontend/src/**/*",
+    "frontend/test/**/*",
+    "enterprise/frontend/test/**/*"
   ],
-  "exclude": ["node_modules"]
+  "exclude": [
+    "node_modules",
+    // The following files will load `frontend/src/metabase/app` which will conflict with
+    // `frontend/src/metabase/App.tsx`, since now we're loading both JS and TS files.
+    "frontend/src/metabase/app-main.js",
+    "frontend/src/metabase/app-public.js",
+    "frontend/src/metabase/app-embed.js"
+  ]
 }