diff --git a/packages/bbui/src/Icon/Icon.svelte b/packages/bbui/src/Icon/Icon.svelte
index 73ad8edd10..c22bb3f918 100644
--- a/packages/bbui/src/Icon/Icon.svelte
+++ b/packages/bbui/src/Icon/Icon.svelte
@@ -10,12 +10,12 @@
   export let size = "M"
   export let hoverable = false
   export let disabled = false
-  export let color
-  export let hoverColor
-  export let tooltip
+  export let color = undefined
+  export let hoverColor = undefined
+  export let tooltip = undefined
   export let tooltipPosition = TooltipPosition.Bottom
   export let tooltipType = TooltipType.Default
-  export let tooltipColor
+  export let tooltipColor = undefined
   export let tooltipWrap = true
   export let newStyles = false
 </script>
diff --git a/packages/builder/src/components/common/CodeEditor/index.ts b/packages/builder/src/components/common/CodeEditor/index.ts
index 0063cfc789..d2cbe8a23e 100644
--- a/packages/builder/src/components/common/CodeEditor/index.ts
+++ b/packages/builder/src/components/common/CodeEditor/index.ts
@@ -4,12 +4,13 @@ import { groupBy } from "lodash"
 import {
   BindingCompletion,
   BindingMode,
+  EditorModesMap,
   Helper,
   Snippet,
 } from "@budibase/types"
 import { CompletionContext } from "@codemirror/autocomplete"
 
-export const EditorModes = {
+export const EditorModes: EditorModesMap = {
   JS: {
     name: "javascript",
     json: false,
diff --git a/packages/builder/src/components/common/bindings/BindingPanel.svelte b/packages/builder/src/components/common/bindings/BindingPanel.svelte
index 29ddb6a1d0..6b9119f433 100644
--- a/packages/builder/src/components/common/bindings/BindingPanel.svelte
+++ b/packages/builder/src/components/common/bindings/BindingPanel.svelte
@@ -37,7 +37,9 @@
     SidePanel,
     BindingCompletion,
     Snippet,
+    Helper,
   } from "@budibase/types"
+  import { CompletionContext } from "@codemirror/autocomplete"
 
   const dispatch = createEventDispatcher()
 
@@ -53,8 +55,8 @@
   export let placeholder = null
   export let showTabBar = true
 
-  let mode: BindingMode
-  let sidePanel: SidePanel
+  let mode: BindingMode | null
+  let sidePanel: SidePanel | null
   let initialValueJS = value?.startsWith?.("{{ js ")
   let jsValue = initialValueJS ? value : null
   let hbsValue = initialValueJS ? null : value
@@ -109,19 +111,19 @@
     snippets: Snippet[] | null,
     useSnippets?: boolean
   ) => {
-    const completions = [
+    const completions: ((_: CompletionContext) => any)[] = [
       jsAutocomplete([
         ...bindingCompletions,
         ...getHelperCompletions(EditorModes.JS),
       ]),
     ]
-    if (useSnippets) {
+    if (useSnippets && snippets) {
       completions.push(snippetAutoComplete(snippets))
     }
     return completions
   }
 
-  const getModeOptions = (allowHBS, allowJS) => {
+  const getModeOptions = (allowHBS: boolean, allowJS: boolean) => {
     let options = []
     if (allowHBS) {
       options.push(BindingMode.Text)
@@ -132,7 +134,12 @@
     return options
   }
 
-  const getSidePanelOptions = (bindings, context, useSnippets, mode) => {
+  const getSidePanelOptions = (
+    bindings: EnrichedBinding[],
+    context: any,
+    useSnippets: boolean,
+    mode: BindingMode | null
+  ) => {
     let options = []
     if (bindings?.length) {
       options.push(SidePanel.Bindings)
@@ -146,32 +153,39 @@
     return options
   }
 
-  const debouncedEval = Utils.debounce((expression, context, snippets) => {
-    try {
-      expressionError = null
-      expressionResult = processStringSync(
-        expression || "",
-        {
-          ...context,
-          snippets,
-        },
-        {
-          noThrow: false,
-        }
-      )
-    } catch (err) {
-      expressionResult = null
-      expressionError = err
-    }
-    evaluating = false
-  }, 260)
+  const debouncedEval = Utils.debounce(
+    (expression: string | null, context: any, snippets: Snippet[]) => {
+      try {
+        expressionError = null
+        expressionResult = processStringSync(
+          expression || "",
+          {
+            ...context,
+            snippets,
+          },
+          {
+            noThrow: false,
+          }
+        )
+      } catch (err: any) {
+        expressionResult = null
+        expressionError = err
+      }
+      evaluating = false
+    },
+    260
+  )
 
-  const requestEval = (expression, context, snippets) => {
+  const requestEval = (
+    expression: string | null,
+    context: any,
+    snippets: Snippet[] | null
+  ) => {
     evaluating = true
     debouncedEval(expression, context, snippets)
   }
 
-  const highlightJSON = json => {
+  const highlightJSON = (json: object | string) => {
     return JsonFormatter.format(json, {
       keyColor: "#e06c75",
       numberColor: "#e5c07b",
@@ -182,7 +196,11 @@
     })
   }
 
-  const enrichBindings = (bindings, context, snippets) => {
+  const enrichBindings = (
+    bindings: EnrichedBinding[],
+    context: any,
+    snippets: Snippet[] | null
+  ) => {
     // Create a single big array to enrich in one go
     const bindingStrings = bindings.map(binding => {
       if (binding.runtimeBinding.startsWith('trim "')) {
@@ -193,17 +211,18 @@
         return `{{ literal ${binding.runtimeBinding} }}`
       }
     })
-    const bindingEvauations = processObjectSync(bindingStrings, {
+    const bindingEvaluations = processObjectSync(bindingStrings, {
       ...context,
       snippets,
     })
 
     // Enrich bindings with evaluations and highlighted HTML
     return bindings.map((binding, idx) => {
-      if (!context) {
+      if (!context || typeof bindingEvaluations !== "object") {
         return binding
       }
-      const value = JSON.stringify(bindingEvauations[idx], null, 2)
+      const evalObj: Record<any, any> = bindingEvaluations
+      const value = JSON.stringify(evalObj[idx], null, 2)
       return {
         ...binding,
         value,
@@ -212,29 +231,38 @@
     })
   }
 
-  const updateValue = val => {
+  const updateValue = (val: any) => {
     const runtimeExpression = readableToRuntimeBinding(enrichedBindings, val)
     dispatch("change", val)
     requestEval(runtimeExpression, context, snippets)
   }
 
-  const onSelectHelper = (helper, js) => {
-    bindingHelpers.onSelectHelper(js ? jsValue : hbsValue, helper, { js })
+  const onSelectHelper = (helper: Helper, js?: boolean) => {
+    bindingHelpers.onSelectHelper(js ? jsValue : hbsValue, helper, {
+      js,
+      dontDecode: undefined,
+    })
   }
 
-  const onSelectBinding = (binding, { forceJS } = {}) => {
+  const onSelectBinding = (
+    binding: EnrichedBinding,
+    { forceJS }: { forceJS?: boolean } = {}
+  ) => {
     const js = usingJS || forceJS
-    bindingHelpers.onSelectBinding(js ? jsValue : hbsValue, binding, { js })
+    bindingHelpers.onSelectBinding(js ? jsValue : hbsValue, binding, {
+      js,
+      dontDecode: undefined,
+    })
   }
 
-  const changeMode = newMode => {
+  const changeMode = (newMode: BindingMode) => {
     if (targetMode || newMode === mode) {
       return
     }
 
     // Get the raw editor value to see if we are abandoning changes
     let rawValue = editorValue
-    if (mode === BindingMode.JavaScript) {
+    if (mode === BindingMode.JavaScript && rawValue) {
       rawValue = decodeJSBinding(rawValue)
     }
 
@@ -253,16 +281,16 @@
     targetMode = null
   }
 
-  const changeSidePanel = newSidePanel => {
+  const changeSidePanel = (newSidePanel: SidePanel) => {
     sidePanel = newSidePanel === sidePanel ? null : newSidePanel
   }
 
-  const onChangeHBSValue = e => {
+  const onChangeHBSValue = (e: { detail: string }) => {
     hbsValue = e.detail
     updateValue(hbsValue)
   }
 
-  const onChangeJSValue = e => {
+  const onChangeJSValue = (e: { detail: string }) => {
     jsValue = encodeJSBinding(e.detail)
     if (!e.detail?.trim()) {
       // Don't bother saving empty values as JS
diff --git a/packages/frontend-core/src/utils/utils.js b/packages/frontend-core/src/utils/utils.js
index 55603b0129..f0635fbeac 100644
--- a/packages/frontend-core/src/utils/utils.js
+++ b/packages/frontend-core/src/utils/utils.js
@@ -43,7 +43,7 @@ export const sequential = fn => {
  * invocations is enforced.
  * @param callback an async function to run
  * @param minDelay the minimum delay between invocations
- * @returns {Promise} a debounced version of the callback
+ * @returns a debounced version of the callback
  */
 export const debounce = (callback, minDelay = 1000) => {
   let timeout
diff --git a/packages/types/src/ui/components/codeEditor.ts b/packages/types/src/ui/components/codeEditor.ts
new file mode 100644
index 0000000000..8ea885b667
--- /dev/null
+++ b/packages/types/src/ui/components/codeEditor.ts
@@ -0,0 +1,19 @@
+type EditorMode =
+  | {
+      key: "JS"
+      name: "javascript"
+      json: boolean
+      match: RegExp
+    }
+  | {
+      key: "Handlebars"
+      name: "handlebars"
+      base: "text/html"
+      match: RegExp
+    }
+  | {
+      key: "Text"
+      name: "text/html"
+    }
+
+export type EditorModesMap = { [M in EditorMode as M["key"]]: Omit<M, "key"> }
diff --git a/packages/types/src/ui/components/index.ts b/packages/types/src/ui/components/index.ts
index 6638d36b32..8dc1638f8c 100644
--- a/packages/types/src/ui/components/index.ts
+++ b/packages/types/src/ui/components/index.ts
@@ -1 +1,2 @@
 export * from "./sidepanel"
+export * from "./codeEditor"