From bafce11164bd9676afc78bc2af4fd65f1707d327 Mon Sep 17 00:00:00 2001
From: Dalton <daltojohnso@users.noreply.github.com>
Date: Fri, 7 Oct 2022 09:24:11 -0400
Subject: [PATCH] Move type functions in `schema_metadata.js` to `metabase-lib`
 (#25822)

* Move some constants in schema_metadata to metabase-lib

* Rename isFK and isPK to isTypeFK, isTypePK to handle name conflicts

* Move type logic in schema_metadata.js to metabase-lib

* Move unit test over to isa.unit.spec.js
---
 frontend/src/metabase-lib/lib/Question.ts     |   2 +-
 .../src/metabase-lib/lib/metadata/Field.ts    |  12 +-
 .../lib/queries/drills/foreign-key-drill.js   |   4 +-
 .../lib/queries/drills/pivot-drill.js         |   6 +-
 .../lib/queries/drills/quick-filter-drill.js  |  11 +-
 .../metabase-lib/lib/queries/utils/actions.js |   3 +-
 .../lib/queries/utils/drilldown.js            |   9 +-
 .../metabase-lib/lib/queries/utils/filter.js  |   3 +-
 .../src/metabase-lib/lib/types/constants.js   |  80 +++++
 .../src/metabase-lib/lib/types/utils/isa.js   | 189 +++++++++++-
 .../lib/types/utils/isa.unit.spec.js          | 101 +++++++
 .../database/ColumnItem/ColumnItem.jsx        |  13 +-
 .../admin/datamodel/containers/FieldApp.jsx   |   5 +-
 frontend/src/metabase/lib/click-behavior.js   |   3 +-
 frontend/src/metabase/lib/formatting/date.tsx |   2 +-
 .../src/metabase/lib/formatting/geography.ts  |   2 +-
 frontend/src/metabase/lib/formatting/link.ts  |   3 +-
 frontend/src/metabase/lib/formatting/url.tsx  |   2 +-
 .../src/metabase/lib/formatting/value.tsx     |   8 +-
 frontend/src/metabase/lib/schema_metadata.js  | 276 ++----------------
 .../components/drill/ObjectDetailDrill.jsx    |   2 +-
 .../metabase/parameters/utils/operators.ts    |   4 +-
 .../DatasetFieldMetadataSidebar.jsx           |   4 +-
 .../modals/BulkFilterItem/BulkFilterItem.tsx  |   6 +-
 .../filters/modals/BulkFilterList/utils.ts    |   4 +-
 .../BulkFilterSelect/BulkFilterSelect.tsx     |   2 +-
 .../filters/modals/InlineValuePicker/utils.ts |   2 +-
 .../filters/pickers/DefaultPicker.jsx         |   2 +-
 .../src/metabase/query_builder/selectors.js   |   2 +-
 .../metabase/reference/components/Field.jsx   |   8 +-
 .../reference/components/FieldTypeDetail.jsx  |   9 +-
 frontend/src/metabase/reference/utils.js      |   7 +-
 .../components/ChoroplethMap.jsx              |   2 +-
 .../components/LeafletGridHeatMap.jsx         |   2 +-
 .../components/LeafletMarkerPinMap.jsx        |   2 +-
 .../components/LineAreaBarChart.jsx           |  28 +-
 .../components/ObjectDetail/ObjectDetail.tsx  |   3 +-
 .../ObjectDetail/ObjectDetailsTable.tsx       |   3 +-
 .../components/ObjectDetail/utils.ts          |   2 +-
 .../visualizations/components/PinMap.jsx      |   2 +-
 .../TableInteractive/TableInteractive.jsx     |   2 +-
 .../components/TableSimple/TableCell.jsx      |   2 +-
 .../components/TableSimple/TableSimple.jsx    |   2 +-
 .../settings/ChartSettingsTableFormatting.jsx |   2 +-
 .../metabase/visualizations/lib/numeric.js    |   2 +-
 .../visualizations/lib/settings/column.js     |  15 +-
 .../visualizations/lib/settings/graph.js      |  12 +-
 .../visualizations/lib/settings/utils.js      |   2 +-
 .../src/metabase/visualizations/lib/table.js  |   2 +-
 .../metabase/visualizations/lib/timeseries.js |   2 +-
 .../src/metabase/visualizations/lib/utils.js  |   6 +-
 .../visualizations/visualizations/Gauge.jsx   |   2 +-
 .../visualizations/visualizations/List.tsx    |  14 +-
 .../visualizations/visualizations/Map.jsx     |  18 +-
 .../visualizations/PivotTable.jsx             |   2 +-
 .../visualizations/Progress.jsx               |   2 +-
 .../visualizations/SmartScalar.jsx            |   2 +-
 .../visualizations/visualizations/Table.jsx   |  18 +-
 .../metabase/lib/schema_metadata.unit.spec.js | 105 +------
 59 files changed, 550 insertions(+), 492 deletions(-)
 create mode 100644 frontend/src/metabase-lib/lib/types/utils/isa.unit.spec.js

diff --git a/frontend/src/metabase-lib/lib/Question.ts b/frontend/src/metabase-lib/lib/Question.ts
index 92832a3aef7..0102ebf8982 100644
--- a/frontend/src/metabase-lib/lib/Question.ts
+++ b/frontend/src/metabase-lib/lib/Question.ts
@@ -24,7 +24,7 @@ import {
 } from "metabase-lib/lib/Dimension";
 import Mode from "metabase-lib/lib/Mode";
 import { isStandard } from "metabase-lib/lib/queries/utils/filter";
-import { isFK } from "metabase/lib/schema_metadata";
+import { isFK } from "metabase-lib/lib/types/utils/isa";
 import { memoizeClass, sortObject } from "metabase-lib/lib/utils";
 /* eslint-enable import/order */
 
diff --git a/frontend/src/metabase-lib/lib/metadata/Field.ts b/frontend/src/metabase-lib/lib/metadata/Field.ts
index 7423ab83eff..ed1d617bc27 100644
--- a/frontend/src/metabase-lib/lib/metadata/Field.ts
+++ b/frontend/src/metabase-lib/lib/metadata/Field.ts
@@ -4,6 +4,12 @@ import _ from "underscore";
 import moment from "moment-timezone";
 
 import { formatField, stripId } from "metabase/lib/formatting";
+import {
+  getIconForField,
+  getFilterOperators,
+} from "metabase/lib/schema_metadata";
+import type { FieldFingerprint } from "metabase-types/api/field";
+import type { Field as FieldRef } from "metabase-types/types/Query";
 import {
   isDate,
   isTime,
@@ -28,11 +34,7 @@ import {
   isPK,
   isFK,
   isEntityName,
-  getIconForField,
-  getFilterOperators,
-} from "metabase/lib/schema_metadata";
-import type { FieldFingerprint } from "metabase-types/api/field";
-import type { Field as FieldRef } from "metabase-types/types/Query";
+} from "metabase-lib/lib/types/utils/isa";
 import { getFieldValues } from "metabase-lib/lib/queries/utils/field";
 import { createLookupByProperty, memoizeClass } from "metabase-lib/lib/utils";
 import type StructuredQuery from "metabase-lib/lib/queries/StructuredQuery";
diff --git a/frontend/src/metabase-lib/lib/queries/drills/foreign-key-drill.js b/frontend/src/metabase-lib/lib/queries/drills/foreign-key-drill.js
index deff4b191cf..6f1c8884e30 100644
--- a/frontend/src/metabase-lib/lib/queries/drills/foreign-key-drill.js
+++ b/frontend/src/metabase-lib/lib/queries/drills/foreign-key-drill.js
@@ -1,5 +1,5 @@
 import { stripId } from "metabase/lib/formatting/strings";
-import { isFK, isPK } from "metabase-lib/lib/types/utils/isa";
+import { isTypeFK, isTypePK } from "metabase-lib/lib/types/utils/isa";
 
 export function foreignKeyDrill({ question, clicked }) {
   const query = question.query();
@@ -14,7 +14,7 @@ export function foreignKeyDrill({ question, clicked }) {
   }
 
   const { column } = clicked;
-  if (isPK(column.semantic_type) || !isFK(column.semantic_type)) {
+  if (isTypePK(column.semantic_type) || !isTypeFK(column.semantic_type)) {
     return null;
   }
 
diff --git a/frontend/src/metabase-lib/lib/queries/drills/pivot-drill.js b/frontend/src/metabase-lib/lib/queries/drills/pivot-drill.js
index 55753f00ad0..6a645638e6a 100644
--- a/frontend/src/metabase-lib/lib/queries/drills/pivot-drill.js
+++ b/frontend/src/metabase-lib/lib/queries/drills/pivot-drill.js
@@ -1,4 +1,8 @@
-import { isAddress, isCategory, isDate } from "metabase/lib/schema_metadata";
+import {
+  isAddress,
+  isCategory,
+  isDate,
+} from "metabase-lib/lib/types/utils/isa";
 
 function pivotDrill({ question, clicked, fieldFilter }) {
   const query = question.query();
diff --git a/frontend/src/metabase-lib/lib/queries/drills/quick-filter-drill.js b/frontend/src/metabase-lib/lib/queries/drills/quick-filter-drill.js
index 0fcef5f8f71..ce3f0ca1b73 100644
--- a/frontend/src/metabase-lib/lib/queries/drills/quick-filter-drill.js
+++ b/frontend/src/metabase-lib/lib/queries/drills/quick-filter-drill.js
@@ -1,5 +1,10 @@
-import { isDate, isNumeric } from "metabase/lib/schema_metadata";
-import { isa, isFK, isPK } from "metabase-lib/lib/types/utils/isa";
+import {
+  isa,
+  isTypeFK,
+  isTypePK,
+  isDate,
+  isNumeric,
+} from "metabase-lib/lib/types/utils/isa";
 import { TYPE } from "metabase-lib/lib/types/constants";
 import { isLocalField } from "metabase-lib/lib/queries/utils";
 
@@ -18,7 +23,7 @@ export function quickFilterDrill({ question, clicked }) {
   }
 
   const { column } = clicked;
-  if (isPK(column.semantic_type) || isFK(column.semantic_type)) {
+  if (isTypePK(column.semantic_type) || isTypeFK(column.semantic_type)) {
     return null;
   }
 
diff --git a/frontend/src/metabase-lib/lib/queries/utils/actions.js b/frontend/src/metabase-lib/lib/queries/utils/actions.js
index 2e4d8d0181e..b0cb1497285 100644
--- a/frontend/src/metabase-lib/lib/queries/utils/actions.js
+++ b/frontend/src/metabase-lib/lib/queries/utils/actions.js
@@ -1,8 +1,7 @@
 import moment from "moment-timezone";
 
-import { isDate, isNumber } from "metabase/lib/schema_metadata";
-
 import { parseTimestamp } from "metabase/lib/time";
+import { isDate, isNumber } from "metabase-lib/lib/types/utils/isa";
 import {
   rangeForValue,
   fieldRefForColumn,
diff --git a/frontend/src/metabase-lib/lib/queries/utils/drilldown.js b/frontend/src/metabase-lib/lib/queries/utils/drilldown.js
index 9b3617d0ffa..7194a1e1149 100644
--- a/frontend/src/metabase-lib/lib/queries/utils/drilldown.js
+++ b/frontend/src/metabase-lib/lib/queries/utils/drilldown.js
@@ -1,6 +1,11 @@
 import _ from "underscore";
-import { isLatitude, isLongitude, isDate } from "metabase/lib/schema_metadata";
-import { isa } from "metabase-lib/lib/types/utils/isa";
+
+import {
+  isa,
+  isLatitude,
+  isLongitude,
+  isDate,
+} from "metabase-lib/lib/types/utils/isa";
 import { TYPE } from "metabase-lib/lib/types/constants";
 
 import { isExpressionField } from "metabase-lib/lib/queries/utils/field-ref";
diff --git a/frontend/src/metabase-lib/lib/queries/utils/filter.js b/frontend/src/metabase-lib/lib/queries/utils/filter.js
index e84529ba503..c8c397320b6 100644
--- a/frontend/src/metabase-lib/lib/queries/utils/filter.js
+++ b/frontend/src/metabase-lib/lib/queries/utils/filter.js
@@ -4,7 +4,8 @@ import {
   FILTER_OPERATORS,
   isLiteral,
 } from "metabase/lib/expressions";
-import { STRING, getOperatorByTypeAndName } from "metabase/lib/schema_metadata";
+import { getOperatorByTypeAndName } from "metabase/lib/schema_metadata";
+import { STRING } from "metabase-lib/lib/types/constants";
 import { isStartingFrom } from "metabase-lib/lib/queries/utils/query-time";
 import { op, args, noNullValues, add, update, remove, clear } from "./util";
 import { isValidField } from "./field-ref";
diff --git a/frontend/src/metabase-lib/lib/types/constants.js b/frontend/src/metabase-lib/lib/types/constants.js
index b1b8c4a3bd1..48b68a7db75 100644
--- a/frontend/src/metabase-lib/lib/types/constants.js
+++ b/frontend/src/metabase-lib/lib/types/constants.js
@@ -1,3 +1,83 @@
 import { TYPE as cljs_TYPE } from "cljs/metabase.types";
 
 export const TYPE = cljs_TYPE;
+
+// primary field types used for picking operators, etc
+export const NUMBER = "NUMBER";
+export const STRING = "STRING";
+export const STRING_LIKE = "STRING_LIKE";
+export const BOOLEAN = "BOOLEAN";
+export const TEMPORAL = "TEMPORAL";
+export const LOCATION = "LOCATION";
+export const COORDINATE = "COORDINATE";
+export const FOREIGN_KEY = "FOREIGN_KEY";
+export const PRIMARY_KEY = "PRIMARY_KEY";
+
+// other types used for various purposes
+export const ENTITY = "ENTITY";
+export const SUMMABLE = "SUMMABLE";
+export const SCOPE = "SCOPE";
+export const CATEGORY = "CATEGORY";
+export const DIMENSION = "DIMENSION";
+
+export const UNKNOWN = "UNKNOWN";
+
+// NOTE: be sure not to create cycles using the "other" types
+export const TYPE_HIERARCHIES = {
+  [TEMPORAL]: {
+    base: [TYPE.Temporal],
+    effective: [TYPE.Temporal],
+    semantic: [TYPE.Temporal],
+  },
+  [NUMBER]: {
+    base: [TYPE.Number],
+    effective: [TYPE.Number],
+    semantic: [TYPE.Number],
+  },
+  [STRING]: {
+    base: [TYPE.Text],
+    effective: [TYPE.Text],
+    semantic: [TYPE.Text, TYPE.Category],
+  },
+  [STRING_LIKE]: {
+    base: [TYPE.TextLike],
+    effective: [TYPE.TextLike],
+  },
+  [BOOLEAN]: {
+    base: [TYPE.Boolean],
+    effective: [TYPE.Boolean],
+  },
+  [COORDINATE]: {
+    semantic: [TYPE.Coordinate],
+  },
+  [LOCATION]: {
+    semantic: [TYPE.Address],
+  },
+  [ENTITY]: {
+    semantic: [TYPE.FK, TYPE.PK, TYPE.Name],
+  },
+  [FOREIGN_KEY]: {
+    semantic: [TYPE.FK],
+  },
+  [PRIMARY_KEY]: {
+    semantic: [TYPE.PK],
+  },
+  [SUMMABLE]: {
+    include: [NUMBER],
+    exclude: [ENTITY, LOCATION, TEMPORAL],
+  },
+  [SCOPE]: {
+    include: [NUMBER, TEMPORAL, CATEGORY, ENTITY, STRING],
+    exclude: [LOCATION],
+  },
+  [CATEGORY]: {
+    base: [TYPE.Boolean],
+    effective: [TYPE.Boolean],
+    semantic: [TYPE.Category],
+    include: [LOCATION],
+  },
+  // NOTE: this is defunct right now.  see definition of isDimension below.
+  [DIMENSION]: {
+    include: [TEMPORAL, CATEGORY, ENTITY],
+  },
+};
diff --git a/frontend/src/metabase-lib/lib/types/utils/isa.js b/frontend/src/metabase-lib/lib/types/utils/isa.js
index 47403d1ddba..e1014235100 100644
--- a/frontend/src/metabase-lib/lib/types/utils/isa.js
+++ b/frontend/src/metabase-lib/lib/types/utils/isa.js
@@ -1,6 +1,21 @@
 import { isa as cljs_isa } from "cljs/metabase.types";
 
-import { TYPE } from "metabase-lib/lib/types/constants";
+import {
+  TYPE,
+  TYPE_HIERARCHIES,
+  TEMPORAL,
+  LOCATION,
+  COORDINATE,
+  FOREIGN_KEY,
+  PRIMARY_KEY,
+  STRING,
+  STRING_LIKE,
+  NUMBER,
+  BOOLEAN,
+  SUMMABLE,
+  SCOPE,
+  CATEGORY,
+} from "metabase-lib/lib/types/constants";
 
 /**
  * Is x the same as, or a descendant type of, y?
@@ -18,10 +33,178 @@ export const isa = (x, y) => cljs_isa(x, y);
 // this will also make it easier to tweak how these checks work in the future,
 // e.g. when we add an `is_pk` column and eliminate the PK semantic type we can just look for places that use isPK
 
-export function isPK(type) {
+export function isTypePK(type) {
   return isa(type, TYPE.PK);
 }
 
-export function isFK(type) {
+export function isTypeFK(type) {
   return isa(type, TYPE.FK);
 }
+
+export function isFieldType(type, field) {
+  if (!field) {
+    return false;
+  }
+
+  const typeDefinition = TYPE_HIERARCHIES[type];
+  // check to see if it belongs to any of the field types:
+  const props = field.effective_type
+    ? ["effective", "semantic"]
+    : ["base", "semantic"];
+  for (const prop of props) {
+    const allowedTypes = typeDefinition[prop];
+    if (!allowedTypes) {
+      continue;
+    }
+
+    const fieldType = field[prop + "_type"];
+    for (const allowedType of allowedTypes) {
+      if (isa(fieldType, allowedType)) {
+        return true;
+      }
+    }
+  }
+
+  // recursively check to see if it's NOT another field type:
+  for (const excludedType of typeDefinition.exclude || []) {
+    if (isFieldType(excludedType, field)) {
+      return false;
+    }
+  }
+
+  // recursively check to see if it's another field type:
+  for (const includedType of typeDefinition.include || []) {
+    if (isFieldType(includedType, field)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+export function getFieldType(field) {
+  // try more specific types first, then more generic types
+  for (const type of [
+    TEMPORAL,
+    LOCATION,
+    COORDINATE,
+    FOREIGN_KEY,
+    PRIMARY_KEY,
+    STRING,
+    STRING_LIKE,
+    NUMBER,
+    BOOLEAN,
+  ]) {
+    if (isFieldType(type, field)) {
+      return type;
+    }
+  }
+}
+
+export const isDate = isFieldType.bind(null, TEMPORAL);
+export const isNumeric = isFieldType.bind(null, NUMBER);
+export const isBoolean = isFieldType.bind(null, BOOLEAN);
+export const isString = isFieldType.bind(null, STRING);
+export const isSummable = isFieldType.bind(null, SUMMABLE);
+export const isScope = isFieldType.bind(null, SCOPE);
+export const isCategory = isFieldType.bind(null, CATEGORY);
+export const isLocation = isFieldType.bind(null, LOCATION);
+
+export const isDimension = col =>
+  col && col.source !== "aggregation" && !isDescription(col);
+export const isMetric = col =>
+  col && col.source !== "breakout" && isSummable(col);
+
+export const isFK = field => field && isTypeFK(field.semantic_type);
+export const isPK = field => field && isTypePK(field.semantic_type);
+export const isEntityName = field =>
+  field && isa(field.semantic_type, TYPE.Name);
+
+export const isAny = col => true;
+
+export const isNumericBaseType = field => {
+  if (!field) {
+    return false;
+  }
+  if (field.effective_type) {
+    return isa(field.effective_type, TYPE.Number);
+  } else {
+    return isa(field.base_type, TYPE.Number);
+  }
+};
+
+export const isDateWithoutTime = field => {
+  if (!field) {
+    return false;
+  }
+  if (field.effective_type) {
+    return isa(field.effective_type, TYPE.Date);
+  } else {
+    return isa(field.base_type, TYPE.Date);
+  }
+};
+
+// ZipCode, ID, etc derive from Number but should not be formatted as numbers
+export const isNumber = field =>
+  field &&
+  isNumericBaseType(field) &&
+  (field.semantic_type == null || isa(field.semantic_type, TYPE.Number));
+
+export const isBinnedNumber = field => isNumber(field) && !!field.binning_info;
+
+export const isTime = field => {
+  if (!field) {
+    return false;
+  }
+  if (field.effective_type) {
+    return isa(field.effective_type, TYPE.Time);
+  } else {
+    return isa(field.base_type, TYPE.Time);
+  }
+};
+
+export const isAddress = field =>
+  field && isa(field.semantic_type, TYPE.Address);
+export const isCity = field => field && isa(field.semantic_type, TYPE.City);
+export const isState = field => field && isa(field.semantic_type, TYPE.State);
+export const isZipCode = field =>
+  field && isa(field.semantic_type, TYPE.ZipCode);
+export const isCountry = field =>
+  field && isa(field.semantic_type, TYPE.Country);
+export const isCoordinate = field =>
+  field && isa(field.semantic_type, TYPE.Coordinate);
+export const isLatitude = field =>
+  field && isa(field.semantic_type, TYPE.Latitude);
+export const isLongitude = field =>
+  field && isa(field.semantic_type, TYPE.Longitude);
+
+export const isCurrency = field =>
+  field && isa(field.semantic_type, TYPE.Currency);
+
+export const isDescription = field =>
+  field && isa(field.semantic_type, TYPE.Description);
+
+export const isComment = field =>
+  field && isa(field.semantic_type, TYPE.Comment);
+
+export const isID = field => isFK(field) || isPK(field);
+
+export const isURL = field => field && isa(field.semantic_type, TYPE.URL);
+export const isEmail = field => field && isa(field.semantic_type, TYPE.Email);
+export const isAvatarURL = field =>
+  field && isa(field.semantic_type, TYPE.AvatarURL);
+export const isImageURL = field =>
+  field && isa(field.semantic_type, TYPE.ImageURL);
+
+export function hasLatitudeAndLongitudeColumns(cols) {
+  let hasLatitude = false;
+  let hasLongitude = false;
+  for (const col of cols) {
+    if (isLatitude(col)) {
+      hasLatitude = true;
+    }
+    if (isLongitude(col)) {
+      hasLongitude = true;
+    }
+  }
+  return hasLatitude && hasLongitude;
+}
diff --git a/frontend/src/metabase-lib/lib/types/utils/isa.unit.spec.js b/frontend/src/metabase-lib/lib/types/utils/isa.unit.spec.js
new file mode 100644
index 00000000000..619efcf6324
--- /dev/null
+++ b/frontend/src/metabase-lib/lib/types/utils/isa.unit.spec.js
@@ -0,0 +1,101 @@
+import { getFieldType } from "metabase-lib/lib/types/utils/isa";
+import {
+  TYPE,
+  TEMPORAL,
+  STRING,
+  STRING_LIKE,
+  NUMBER,
+  BOOLEAN,
+  LOCATION,
+  COORDINATE,
+  PRIMARY_KEY,
+} from "metabase-lib/lib/types/constants";
+
+describe("isa", () => {
+  describe("getFieldType", () => {
+    it("should know a date", () => {
+      expect(getFieldType({ base_type: TYPE.Date })).toEqual(TEMPORAL);
+      expect(getFieldType({ base_type: TYPE.DateTime })).toEqual(TEMPORAL);
+      expect(getFieldType({ base_type: TYPE.Time })).toEqual(TEMPORAL);
+      expect(getFieldType({ effective_type: TYPE.Date })).toEqual(TEMPORAL);
+      expect(getFieldType({ effective_type: TYPE.DateTime })).toEqual(TEMPORAL);
+      expect(getFieldType({ effective_type: TYPE.Time })).toEqual(TEMPORAL);
+    });
+
+    it("should know a number", () => {
+      expect(getFieldType({ base_type: TYPE.BigInteger })).toEqual(NUMBER);
+      expect(getFieldType({ base_type: TYPE.Integer })).toEqual(NUMBER);
+      expect(getFieldType({ base_type: TYPE.Float })).toEqual(NUMBER);
+      expect(getFieldType({ base_type: TYPE.Decimal })).toEqual(NUMBER);
+    });
+
+    it("should know a string", () => {
+      expect(getFieldType({ base_type: TYPE.Text })).toEqual(STRING);
+    });
+
+    it("should know things that are types of strings", () => {
+      expect(
+        getFieldType({ base_type: TYPE.Text, semantic_type: TYPE.Name }),
+      ).toEqual(STRING);
+      expect(
+        getFieldType({ base_type: TYPE.Text, semantic_type: TYPE.Description }),
+      ).toEqual(STRING);
+      expect(
+        getFieldType({ base_type: TYPE.Text, semantic_type: TYPE.UUID }),
+      ).toEqual(STRING);
+      expect(
+        getFieldType({ base_type: TYPE.Text, semantic_type: TYPE.URL }),
+      ).toEqual(STRING);
+    });
+
+    it("should know a pk", () => {
+      expect(
+        getFieldType({ base_type: TYPE.Integer, semantic_type: TYPE.PK }),
+      ).toEqual(PRIMARY_KEY);
+    });
+
+    it("should know a bool", () => {
+      expect(getFieldType({ base_type: TYPE.Boolean })).toEqual(BOOLEAN);
+    });
+
+    it("should know a location", () => {
+      expect(getFieldType({ semantic_type: TYPE.City })).toEqual(LOCATION);
+      expect(getFieldType({ semantic_type: TYPE.Country })).toEqual(LOCATION);
+    });
+
+    it("should know a coordinate", () => {
+      expect(getFieldType({ semantic_type: TYPE.Latitude })).toEqual(
+        COORDINATE,
+      );
+      expect(getFieldType({ semantic_type: TYPE.Longitude })).toEqual(
+        COORDINATE,
+      );
+    });
+
+    describe("should know something that is string-like", () => {
+      it("TYPE.TextLike", () => {
+        expect(getFieldType({ base_type: TYPE.TextLike })).toEqual(STRING_LIKE);
+      });
+
+      it("TYPE.IPAddress", () => {
+        expect(getFieldType({ base_type: TYPE.IPAddress })).toEqual(
+          STRING_LIKE,
+        );
+      });
+    });
+
+    it("should still recognize some types as a string regardless of its base type", () => {
+      // TYPE.Float can occur in a field filter
+      expect(
+        getFieldType({ base_type: TYPE.Float, semantic_type: TYPE.Name }),
+      ).toEqual(STRING);
+      expect(
+        getFieldType({ base_type: TYPE.Float, semantic_type: TYPE.Category }),
+      ).toEqual(STRING);
+    });
+
+    it("should know what it doesn't know", () => {
+      expect(getFieldType({ base_type: "DERP DERP DERP" })).toEqual(undefined);
+    });
+  });
+});
diff --git a/frontend/src/metabase/admin/datamodel/components/database/ColumnItem/ColumnItem.jsx b/frontend/src/metabase/admin/datamodel/components/database/ColumnItem/ColumnItem.jsx
index ee3050bbbb3..2f96deaa6ff 100644
--- a/frontend/src/metabase/admin/datamodel/components/database/ColumnItem/ColumnItem.jsx
+++ b/frontend/src/metabase/admin/datamodel/components/database/ColumnItem/ColumnItem.jsx
@@ -9,13 +9,12 @@ import cx from "classnames";
 import Select, { Option } from "metabase/core/components/Select";
 import Button from "metabase/core/components/Button";
 import * as MetabaseCore from "metabase/lib/core";
-import { isCurrency } from "metabase/lib/schema_metadata";
 import { getGlobalSettingsForColumn } from "metabase/visualizations/lib/settings/column";
 
 import { currency } from "cljs/metabase.shared.util.currency";
 
 import * as MetabaseAnalytics from "metabase/lib/analytics";
-import { isFK } from "metabase-lib/lib/types/utils/isa";
+import { isTypeFK, isCurrency } from "metabase-lib/lib/types/utils/isa";
 import { getFieldRawName } from "../../../utils";
 import { ColumnItemInput } from "./ColumnItem.styled";
 
@@ -114,7 +113,7 @@ export default withRouter(Column);
 const getFkFieldPlaceholder = (field, idfields) => {
   const hasIdFields = idfields?.length > 0;
   const isRestrictedFKTargedSelected =
-    isFK(field.semantic_type) &&
+    isTypeFK(field.semantic_type) &&
     field.fk_target_field_id != null &&
     !idfields?.some(idField => idField.id === field.fk_target_field_id);
 
@@ -153,7 +152,11 @@ export class SemanticTypeAndTargetPicker extends Component {
     const { field, updateField } = this.props;
 
     // If we are changing the field from a FK to something else, we should delete any FKs present
-    if (field.target && field.target.id != null && isFK(field.semantic_type)) {
+    if (
+      field.target &&
+      field.target.id != null &&
+      isTypeFK(field.semantic_type)
+    ) {
       await updateField({
         semantic_type,
         fk_target_field_id: null,
@@ -203,7 +206,7 @@ export class SemanticTypeAndTargetPicker extends Component {
     ];
 
     const hasIdFields = idfields?.length > 0;
-    const showFKTargetSelect = isFK(field.semantic_type);
+    const showFKTargetSelect = isTypeFK(field.semantic_type);
     const showCurrencyTypeSelect = isCurrency(field);
 
     // If all FK target fields are in the same schema (like `PUBLIC` for sample database)
diff --git a/frontend/src/metabase/admin/datamodel/containers/FieldApp.jsx b/frontend/src/metabase/admin/datamodel/containers/FieldApp.jsx
index 9a8e47a9d70..b1b808e1c2c 100644
--- a/frontend/src/metabase/admin/datamodel/containers/FieldApp.jsx
+++ b/frontend/src/metabase/admin/datamodel/containers/FieldApp.jsx
@@ -34,12 +34,11 @@ import { getMetadata } from "metabase/selectors/metadata";
 // LIB
 import { has_field_values_options } from "metabase/lib/core";
 import { getGlobalSettingsForColumn } from "metabase/visualizations/lib/settings/column";
-import { isCurrency } from "metabase/lib/schema_metadata";
 
 import Databases from "metabase/entities/databases";
 import Tables from "metabase/entities/tables";
 import Fields from "metabase/entities/fields";
-import { isFK } from "metabase-lib/lib/types/utils/isa";
+import { isTypeFK, isCurrency } from "metabase-lib/lib/types/utils/isa";
 import { rescanFieldValues, discardFieldValues } from "../field";
 import UpdateCachedFieldValues from "../components/UpdateCachedFieldValues";
 import FieldRemapping from "../components/FieldRemapping";
@@ -298,7 +297,7 @@ const FieldGeneralPane = ({
       />
     </Section>
 
-    {!isFK(field.semantic_type) && is_coerceable(field.base_type) && (
+    {!isTypeFK(field.semantic_type) && is_coerceable(field.base_type) && (
       <Section>
         <SectionHeader title={t`Cast to a specific data type`} />
         <Select
diff --git a/frontend/src/metabase/lib/click-behavior.js b/frontend/src/metabase/lib/click-behavior.js
index 23c68d91c74..f76ac2c5d4e 100644
--- a/frontend/src/metabase/lib/click-behavior.js
+++ b/frontend/src/metabase/lib/click-behavior.js
@@ -2,7 +2,6 @@ import _ from "underscore";
 import { getIn } from "icepick";
 import { t, ngettext, msgid } from "ttag";
 
-import { isDate } from "metabase/lib/schema_metadata";
 import { parseTimestamp } from "metabase/lib/time";
 import { formatDateTimeForParameter } from "metabase/lib/formatting/date";
 import {
@@ -10,7 +9,7 @@ import {
   variableFilterForParameter,
 } from "metabase/parameters/utils/filters";
 import { isValidImplicitActionClickBehavior } from "metabase/writeback/utils";
-import { isa } from "metabase-lib/lib/types/utils/isa";
+import { isa, isDate } from "metabase-lib/lib/types/utils/isa";
 import { TYPE } from "metabase-lib/lib/types/constants";
 import Question from "metabase-lib/lib/Question";
 import TemplateTagVariable from "metabase-lib/lib/variables/TemplateTagVariable";
diff --git a/frontend/src/metabase/lib/formatting/date.tsx b/frontend/src/metabase/lib/formatting/date.tsx
index 666bccdcbc4..91055db30db 100644
--- a/frontend/src/metabase/lib/formatting/date.tsx
+++ b/frontend/src/metabase/lib/formatting/date.tsx
@@ -2,8 +2,8 @@ import React from "react";
 import moment, { Moment } from "moment-timezone";
 
 import { parseTimestamp } from "metabase/lib/time";
-import { isDateWithoutTime } from "metabase/lib/schema_metadata";
 import type { DatetimeUnit } from "metabase-types/api/query";
+import { isDateWithoutTime } from "metabase-lib/lib/types/utils/isa";
 import {
   DEFAULT_DATE_STYLE,
   DEFAULT_TIME_STYLE,
diff --git a/frontend/src/metabase/lib/formatting/geography.ts b/frontend/src/metabase/lib/formatting/geography.ts
index 92511fbd9f5..a7faf2968cc 100644
--- a/frontend/src/metabase/lib/formatting/geography.ts
+++ b/frontend/src/metabase/lib/formatting/geography.ts
@@ -1,7 +1,7 @@
 import d3 from "d3";
 
-import { isLatitude, isLongitude } from "metabase/lib/schema_metadata";
 import { decimalCount } from "metabase/visualizations/lib/numeric";
+import { isLatitude, isLongitude } from "metabase-lib/lib/types/utils/isa";
 import { OptionsType } from "./types";
 
 const DECIMAL_DEGREES_FORMATTER = d3.format(".08f");
diff --git a/frontend/src/metabase/lib/formatting/link.ts b/frontend/src/metabase/lib/formatting/link.ts
index 74811d8f72c..b3a85604c3e 100644
--- a/frontend/src/metabase/lib/formatting/link.ts
+++ b/frontend/src/metabase/lib/formatting/link.ts
@@ -1,7 +1,6 @@
-import { isDate } from "metabase/lib/schema_metadata";
-
 import { formatValue } from "metabase/lib/formatting";
 import type { DatasetColumn } from "metabase-types/api/dataset";
+import { isDate } from "metabase-lib/lib/types/utils/isa";
 import { formatDateTimeForParameter } from "./date";
 
 interface TemplateForClickFormatFunctionParamsType {
diff --git a/frontend/src/metabase/lib/formatting/url.tsx b/frontend/src/metabase/lib/formatting/url.tsx
index 1440a2c06a1..0d67dc1e378 100644
--- a/frontend/src/metabase/lib/formatting/url.tsx
+++ b/frontend/src/metabase/lib/formatting/url.tsx
@@ -2,7 +2,7 @@ import React from "react";
 
 import ExternalLink from "metabase/core/components/ExternalLink";
 import { getDataFromClicked } from "metabase/lib/click-behavior";
-import { isURL } from "metabase/lib/schema_metadata";
+import { isURL } from "metabase-lib/lib/types/utils/isa";
 import { renderLinkTextForClick, renderLinkURLForClick } from "./link";
 import { formatValue, getRemappedValue } from "./value";
 
diff --git a/frontend/src/metabase/lib/formatting/value.tsx b/frontend/src/metabase/lib/formatting/value.tsx
index 4eba69c08ce..2f281974f40 100644
--- a/frontend/src/metabase/lib/formatting/value.tsx
+++ b/frontend/src/metabase/lib/formatting/value.tsx
@@ -8,6 +8,9 @@ import {
   clickBehaviorIsValid,
   getDataFromClicked,
 } from "metabase/lib/click-behavior";
+import { renderLinkTextForClick } from "metabase/lib/formatting/link";
+import { NULL_DISPLAY_VALUE, NULL_NUMERIC_VALUE } from "metabase/lib/constants";
+import { rangeForValue } from "metabase-lib/lib/queries/utils/dataset";
 import {
   isBoolean,
   isCoordinate,
@@ -16,10 +19,7 @@ import {
   isNumber,
   isTime,
   isURL,
-} from "metabase/lib/schema_metadata";
-import { renderLinkTextForClick } from "metabase/lib/formatting/link";
-import { NULL_DISPLAY_VALUE, NULL_NUMERIC_VALUE } from "metabase/lib/constants";
-import { rangeForValue } from "metabase-lib/lib/queries/utils/dataset";
+} from "metabase-lib/lib/types/utils/isa";
 import { formatEmail } from "./email";
 import { formatTime } from "./time";
 import { formatUrl } from "./url";
diff --git a/frontend/src/metabase/lib/schema_metadata.js b/frontend/src/metabase/lib/schema_metadata.js
index ac4e2e3dc3b..7099ca19b56 100644
--- a/frontend/src/metabase/lib/schema_metadata.js
+++ b/frontend/src/metabase/lib/schema_metadata.js
@@ -2,246 +2,28 @@ import _ from "underscore";
 import { t } from "ttag";
 import { field_semantic_types_map } from "metabase/lib/core";
 import {
-  isa,
-  isFK as isTypeFK,
-  isPK as isTypePK,
+  isNumeric,
+  isDate,
+  isBoolean,
+  isScope,
+  isSummable,
+  getFieldType,
+  isFieldType,
+  isLongitude,
 } from "metabase-lib/lib/types/utils/isa";
-import { TYPE } from "metabase-lib/lib/types/constants";
-
-// primary field types used for picking operators, etc
-export const NUMBER = "NUMBER";
-export const STRING = "STRING";
-export const STRING_LIKE = "STRING_LIKE";
-export const BOOLEAN = "BOOLEAN";
-export const TEMPORAL = "TEMPORAL";
-export const LOCATION = "LOCATION";
-export const COORDINATE = "COORDINATE";
-export const FOREIGN_KEY = "FOREIGN_KEY";
-export const PRIMARY_KEY = "PRIMARY_KEY";
-
-// other types used for various purporses
-export const ENTITY = "ENTITY";
-export const SUMMABLE = "SUMMABLE";
-export const SCOPE = "SCOPE";
-export const CATEGORY = "CATEGORY";
-export const DIMENSION = "DIMENSION";
-
-export const UNKNOWN = "UNKNOWN";
-
-// define various type hierarchies
-// NOTE: be sure not to create cycles using the "other" types
-const TYPES = {
-  [TEMPORAL]: {
-    base: [TYPE.Temporal],
-    effective: [TYPE.Temporal],
-    semantic: [TYPE.Temporal],
-  },
-  [NUMBER]: {
-    base: [TYPE.Number],
-    effective: [TYPE.Number],
-    semantic: [TYPE.Number],
-  },
-  [STRING]: {
-    base: [TYPE.Text],
-    effective: [TYPE.Text],
-    semantic: [TYPE.Text, TYPE.Category],
-  },
-  [STRING_LIKE]: {
-    base: [TYPE.TextLike],
-    effective: [TYPE.TextLike],
-  },
-  [BOOLEAN]: {
-    base: [TYPE.Boolean],
-    effective: [TYPE.Boolean],
-  },
-  [COORDINATE]: {
-    semantic: [TYPE.Coordinate],
-  },
-  [LOCATION]: {
-    semantic: [TYPE.Address],
-  },
-  [ENTITY]: {
-    semantic: [TYPE.FK, TYPE.PK, TYPE.Name],
-  },
-  [FOREIGN_KEY]: {
-    semantic: [TYPE.FK],
-  },
-  [PRIMARY_KEY]: {
-    semantic: [TYPE.PK],
-  },
-  [SUMMABLE]: {
-    include: [NUMBER],
-    exclude: [ENTITY, LOCATION, TEMPORAL],
-  },
-  [SCOPE]: {
-    include: [NUMBER, TEMPORAL, CATEGORY, ENTITY, STRING],
-    exclude: [LOCATION],
-  },
-  [CATEGORY]: {
-    base: [TYPE.Boolean],
-    effective: [TYPE.Boolean],
-    semantic: [TYPE.Category],
-    include: [LOCATION],
-  },
-  // NOTE: this is defunct right now.  see definition of isDimension below.
-  [DIMENSION]: {
-    include: [TEMPORAL, CATEGORY, ENTITY],
-  },
-};
-
-export function isFieldType(type, field) {
-  if (!field) {
-    return false;
-  }
-
-  const typeDefinition = TYPES[type];
-  // check to see if it belongs to any of the field types:
-  const props = field.effective_type
-    ? ["effective", "semantic"]
-    : ["base", "semantic"];
-  for (const prop of props) {
-    const allowedTypes = typeDefinition[prop];
-    if (!allowedTypes) {
-      continue;
-    }
-
-    const fieldType = field[prop + "_type"];
-    for (const allowedType of allowedTypes) {
-      if (isa(fieldType, allowedType)) {
-        return true;
-      }
-    }
-  }
-
-  // recursively check to see if it's NOT another field type:
-  for (const excludedType of typeDefinition.exclude || []) {
-    if (isFieldType(excludedType, field)) {
-      return false;
-    }
-  }
-
-  // recursively check to see if it's another field type:
-  for (const includedType of typeDefinition.include || []) {
-    if (isFieldType(includedType, field)) {
-      return true;
-    }
-  }
-  return false;
-}
-
-export function getFieldType(field) {
-  // try more specific types first, then more generic types
-  for (const type of [
-    TEMPORAL,
-    LOCATION,
-    COORDINATE,
-    FOREIGN_KEY,
-    PRIMARY_KEY,
-    STRING,
-    STRING_LIKE,
-    NUMBER,
-    BOOLEAN,
-  ]) {
-    if (isFieldType(type, field)) {
-      return type;
-    }
-  }
-}
-
-export const isDate = isFieldType.bind(null, TEMPORAL);
-export const isNumeric = isFieldType.bind(null, NUMBER);
-export const isBoolean = isFieldType.bind(null, BOOLEAN);
-export const isString = isFieldType.bind(null, STRING);
-export const isSummable = isFieldType.bind(null, SUMMABLE);
-export const isScope = isFieldType.bind(null, SCOPE);
-export const isCategory = isFieldType.bind(null, CATEGORY);
-export const isLocation = isFieldType.bind(null, LOCATION);
-
-export const isDimension = col =>
-  col && col.source !== "aggregation" && !isDescription(col);
-export const isMetric = col =>
-  col && col.source !== "breakout" && isSummable(col);
-
-export const isFK = field => field && isTypeFK(field.semantic_type);
-export const isPK = field => field && isTypePK(field.semantic_type);
-export const isEntityName = field =>
-  field && isa(field.semantic_type, TYPE.Name);
-
-export const isAny = col => true;
-
-export const isNumericBaseType = field => {
-  if (!field) {
-    return false;
-  }
-  if (field.effective_type) {
-    return isa(field.effective_type, TYPE.Number);
-  } else {
-    return isa(field.base_type, TYPE.Number);
-  }
-};
-
-export const isDateWithoutTime = field => {
-  if (!field) {
-    return false;
-  }
-  if (field.effective_type) {
-    return isa(field.effective_type, TYPE.Date);
-  } else {
-    return isa(field.base_type, TYPE.Date);
-  }
-};
-
-// ZipCode, ID, etc derive from Number but should not be formatted as numbers
-export const isNumber = field =>
-  field &&
-  isNumericBaseType(field) &&
-  (field.semantic_type == null || isa(field.semantic_type, TYPE.Number));
-
-export const isBinnedNumber = field => isNumber(field) && !!field.binning_info;
-
-export const isTime = field => {
-  if (!field) {
-    return false;
-  }
-  if (field.effective_type) {
-    return isa(field.effective_type, TYPE.Time);
-  } else {
-    return isa(field.base_type, TYPE.Time);
-  }
-};
-
-export const isAddress = field =>
-  field && isa(field.semantic_type, TYPE.Address);
-export const isCity = field => field && isa(field.semantic_type, TYPE.City);
-export const isState = field => field && isa(field.semantic_type, TYPE.State);
-export const isZipCode = field =>
-  field && isa(field.semantic_type, TYPE.ZipCode);
-export const isCountry = field =>
-  field && isa(field.semantic_type, TYPE.Country);
-export const isCoordinate = field =>
-  field && isa(field.semantic_type, TYPE.Coordinate);
-export const isLatitude = field =>
-  field && isa(field.semantic_type, TYPE.Latitude);
-export const isLongitude = field =>
-  field && isa(field.semantic_type, TYPE.Longitude);
-
-export const isCurrency = field =>
-  field && isa(field.semantic_type, TYPE.Currency);
-
-export const isDescription = field =>
-  field && isa(field.semantic_type, TYPE.Description);
-
-export const isComment = field =>
-  field && isa(field.semantic_type, TYPE.Comment);
-
-export const isID = field => isFK(field) || isPK(field);
-
-export const isURL = field => field && isa(field.semantic_type, TYPE.URL);
-export const isEmail = field => field && isa(field.semantic_type, TYPE.Email);
-export const isAvatarURL = field =>
-  field && isa(field.semantic_type, TYPE.AvatarURL);
-export const isImageURL = field =>
-  field && isa(field.semantic_type, TYPE.ImageURL);
+import {
+  TYPE,
+  TEMPORAL,
+  LOCATION,
+  COORDINATE,
+  FOREIGN_KEY,
+  PRIMARY_KEY,
+  STRING,
+  STRING_LIKE,
+  NUMBER,
+  BOOLEAN,
+  UNKNOWN,
+} from "metabase-lib/lib/types/constants";
 
 // filter operator argument constructors:
 
@@ -306,7 +88,7 @@ function equivalentArgument(field, table) {
 
 function longitudeFieldSelectArgument(field, table) {
   const values = table.fields
-    .filter(field => isa(field.semantic_type, TYPE.Longitude))
+    .filter(field => isLongitude(field))
     .map(field => ({
       key: field.id,
       name: field.display_name,
@@ -740,20 +522,6 @@ export function addValidOperatorsToFields(table) {
   return table;
 }
 
-export function hasLatitudeAndLongitudeColumns(cols) {
-  let hasLatitude = false;
-  let hasLongitude = false;
-  for (const col of cols) {
-    if (isLatitude(col)) {
-      hasLatitude = true;
-    }
-    if (isLongitude(col)) {
-      hasLongitude = true;
-    }
-  }
-  return hasLatitude && hasLongitude;
-}
-
 export function foreignKeyCountsByOriginTable(fks) {
   if (fks === null || !Array.isArray(fks)) {
     return null;
diff --git a/frontend/src/metabase/modes/components/drill/ObjectDetailDrill.jsx b/frontend/src/metabase/modes/components/drill/ObjectDetailDrill.jsx
index 9308f2fc7ed..ffe185e3332 100644
--- a/frontend/src/metabase/modes/components/drill/ObjectDetailDrill.jsx
+++ b/frontend/src/metabase/modes/components/drill/ObjectDetailDrill.jsx
@@ -1,6 +1,6 @@
 import { t } from "ttag";
-import { isFK, isPK } from "metabase/lib/schema_metadata";
 import { zoomInRow } from "metabase/query_builder/actions";
+import { isFK, isPK } from "metabase-lib/lib/types/utils/isa";
 
 function hasManyPKColumns(question) {
   const fields = question.isDataset()
diff --git a/frontend/src/metabase/parameters/utils/operators.ts b/frontend/src/metabase/parameters/utils/operators.ts
index 92c0d224ccc..290b3a89dbc 100644
--- a/frontend/src/metabase/parameters/utils/operators.ts
+++ b/frontend/src/metabase/parameters/utils/operators.ts
@@ -3,10 +3,8 @@ import { UiParameter } from "metabase/parameters/types";
 import {
   doesOperatorExist,
   getOperatorByTypeAndName,
-  NUMBER,
-  STRING,
-  PRIMARY_KEY,
 } from "metabase/lib/schema_metadata";
+import { NUMBER, STRING, PRIMARY_KEY } from "metabase-lib/lib/types/constants";
 import { PARAMETER_OPERATOR_TYPES } from "../constants";
 import { getParameterType, getParameterSubType } from "./parameter-type";
 
diff --git a/frontend/src/metabase/query_builder/components/DatasetEditor/DatasetFieldMetadataSidebar/DatasetFieldMetadataSidebar.jsx b/frontend/src/metabase/query_builder/components/DatasetEditor/DatasetFieldMetadataSidebar/DatasetFieldMetadataSidebar.jsx
index a8747a2b946..e591edbe851 100644
--- a/frontend/src/metabase/query_builder/components/DatasetEditor/DatasetFieldMetadataSidebar/DatasetFieldMetadataSidebar.jsx
+++ b/frontend/src/metabase/query_builder/components/DatasetEditor/DatasetFieldMetadataSidebar/DatasetFieldMetadataSidebar.jsx
@@ -15,8 +15,7 @@ import {
   field_visibility_types,
   field_semantic_types,
 } from "metabase/lib/core";
-import { isFK, getSemanticTypeIcon } from "metabase/lib/schema_metadata";
-
+import { getSemanticTypeIcon } from "metabase/lib/schema_metadata";
 import RootForm from "metabase/containers/FormikForm";
 import { usePrevious } from "metabase/hooks/use-previous";
 
@@ -26,6 +25,7 @@ import ColumnSettings, {
 } from "metabase/visualizations/components/ColumnSettings";
 import { getGlobalSettingsForColumn } from "metabase/visualizations/lib/settings/column";
 import { isSameField } from "metabase-lib/lib/queries/utils/field-ref";
+import { isFK } from "metabase-lib/lib/types/utils/isa";
 
 import { EDITOR_TAB_INDEXES } from "../constants";
 import MappedFieldPicker from "./MappedFieldPicker";
diff --git a/frontend/src/metabase/query_builder/components/filters/modals/BulkFilterItem/BulkFilterItem.tsx b/frontend/src/metabase/query_builder/components/filters/modals/BulkFilterItem/BulkFilterItem.tsx
index fbe9ff94426..9dafd37114d 100644
--- a/frontend/src/metabase/query_builder/components/filters/modals/BulkFilterItem/BulkFilterItem.tsx
+++ b/frontend/src/metabase/query_builder/components/filters/modals/BulkFilterItem/BulkFilterItem.tsx
@@ -1,10 +1,14 @@
 import React, { useMemo, useCallback } from "react";
 
-import { isBoolean, isString, isNumber } from "metabase/lib/schema_metadata";
 import { BooleanPickerCheckbox } from "metabase/query_builder/components/filters/pickers/BooleanPicker";
 import Filter from "metabase-lib/lib/queries/structured/Filter";
 import Dimension from "metabase-lib/lib/Dimension";
 import StructuredQuery from "metabase-lib/lib/queries/StructuredQuery";
+import {
+  isBoolean,
+  isString,
+  isNumber,
+} from "metabase-lib/lib/types/utils/isa";
 
 import { BulkFilterSelect } from "../BulkFilterSelect";
 import { InlineCategoryPicker } from "../InlineCategoryPicker";
diff --git a/frontend/src/metabase/query_builder/components/filters/modals/BulkFilterList/utils.ts b/frontend/src/metabase/query_builder/components/filters/modals/BulkFilterList/utils.ts
index 2dd4b7b672c..a072818a770 100644
--- a/frontend/src/metabase/query_builder/components/filters/modals/BulkFilterList/utils.ts
+++ b/frontend/src/metabase/query_builder/components/filters/modals/BulkFilterList/utils.ts
@@ -1,8 +1,6 @@
-import { isDate } from "metabase/lib/schema_metadata";
+import { isDate } from "metabase-lib/lib/types/utils/isa";
 import { DimensionOption } from "metabase-lib/lib/queries/StructuredQuery";
 
-import { LONG_TEXT_MIN } from "metabase-lib/lib/metadata/Field";
-
 type PriorityMap = { [key: string]: number | undefined };
 
 const fieldSortPriorities: PriorityMap = {
diff --git a/frontend/src/metabase/query_builder/components/filters/modals/BulkFilterSelect/BulkFilterSelect.tsx b/frontend/src/metabase/query_builder/components/filters/modals/BulkFilterSelect/BulkFilterSelect.tsx
index ffb2bb58b40..0eeaaf8c15f 100644
--- a/frontend/src/metabase/query_builder/components/filters/modals/BulkFilterSelect/BulkFilterSelect.tsx
+++ b/frontend/src/metabase/query_builder/components/filters/modals/BulkFilterSelect/BulkFilterSelect.tsx
@@ -1,9 +1,9 @@
 import React, { useCallback, useMemo } from "react";
 import { t } from "ttag";
 
-import { isBoolean, isDate } from "metabase/lib/schema_metadata";
 import TippyPopoverWithTrigger from "metabase/components/PopoverWithTrigger/TippyPopoverWithTrigger";
 import { DateShortcutOptions } from "metabase/query_builder/components/filters/pickers/DatePicker/DatePickerShortcutOptions";
+import { isBoolean, isDate } from "metabase-lib/lib/types/utils/isa";
 import StructuredQuery, {
   SegmentOption,
 } from "metabase-lib/lib/queries/StructuredQuery";
diff --git a/frontend/src/metabase/query_builder/components/filters/modals/InlineValuePicker/utils.ts b/frontend/src/metabase/query_builder/components/filters/modals/InlineValuePicker/utils.ts
index 5e3548a4eef..c4a00f7829f 100644
--- a/frontend/src/metabase/query_builder/components/filters/modals/InlineValuePicker/utils.ts
+++ b/frontend/src/metabase/query_builder/components/filters/modals/InlineValuePicker/utils.ts
@@ -1,4 +1,4 @@
-import { isString } from "metabase/lib/schema_metadata";
+import { isString } from "metabase-lib/lib/types/utils/isa";
 import type Field from "metabase-lib/lib/metadata/Field";
 import type Filter from "metabase-lib/lib/queries/structured/Filter";
 
diff --git a/frontend/src/metabase/query_builder/components/filters/pickers/DefaultPicker.jsx b/frontend/src/metabase/query_builder/components/filters/pickers/DefaultPicker.jsx
index 210aacfcec3..e3eb4fe03e6 100644
--- a/frontend/src/metabase/query_builder/components/filters/pickers/DefaultPicker.jsx
+++ b/frontend/src/metabase/query_builder/components/filters/pickers/DefaultPicker.jsx
@@ -9,11 +9,11 @@ import FieldValuesWidget from "metabase/components/FieldValuesWidget";
 import {
   getFilterArgumentFormatOptions,
   isFuzzyOperator,
-  isCurrency,
 } from "metabase/lib/schema_metadata";
 
 import { getCurrencySymbol } from "metabase/lib/formatting";
 
+import { isCurrency } from "metabase-lib/lib/types/utils/isa";
 import { keyForColumn } from "metabase-lib/lib/queries/utils/dataset";
 import TextPicker from "./TextPicker";
 import SelectPicker from "./SelectPicker";
diff --git a/frontend/src/metabase/query_builder/selectors.js b/frontend/src/metabase/query_builder/selectors.js
index d97740e0287..ba616f4fb6e 100644
--- a/frontend/src/metabase/query_builder/selectors.js
+++ b/frontend/src/metabase/query_builder/selectors.js
@@ -16,7 +16,6 @@ import { MetabaseApi } from "metabase/services";
 import { getComputedSettingsForSeries } from "metabase/visualizations/lib/settings/visualization";
 import { getCardUiParameters } from "metabase/parameters/utils/cards";
 import { normalizeParameterValue } from "metabase/parameters/utils/parameter-values";
-import { isPK } from "metabase/lib/schema_metadata";
 
 import { isAdHocModelQuestion } from "metabase/lib/data-modeling/utils";
 
@@ -36,6 +35,7 @@ import {
 import ObjectMode from "metabase/modes/components/modes/ObjectMode";
 
 import { LOAD_COMPLETE_FAVICON } from "metabase/hoc/Favicon";
+import { isPK } from "metabase-lib/lib/types/utils/isa";
 import Mode from "metabase-lib/lib/Mode";
 import NativeQuery from "metabase-lib/lib/queries/NativeQuery";
 import Question from "metabase-lib/lib/Question";
diff --git a/frontend/src/metabase/reference/components/Field.jsx b/frontend/src/metabase/reference/components/Field.jsx
index 93748ced060..a6981a5cbef 100644
--- a/frontend/src/metabase/reference/components/Field.jsx
+++ b/frontend/src/metabase/reference/components/Field.jsx
@@ -10,7 +10,7 @@ import * as MetabaseCore from "metabase/lib/core";
 import S from "metabase/components/List.css";
 import Select from "metabase/core/components/Select";
 import Icon from "metabase/components/Icon";
-import { isFK } from "metabase-lib/lib/types/utils/isa";
+import { isTypeFK } from "metabase-lib/lib/types/utils/isa";
 import F from "./Field.css";
 
 const Field = ({ field, foreignKeys, url, icon, isEditing, formField }) => (
@@ -84,8 +84,8 @@ const Field = ({ field, foreignKeys, url, icon, isEditing, formField }) => (
       <div className={cx(S.itemSubtitle, F.fieldSecondary, { mt1: true })}>
         <div className={F.fieldForeignKey}>
           {isEditing
-            ? (isFK(formField.semantic_type.value) ||
-                (isFK(field.semantic_type) &&
+            ? (isTypeFK(formField.semantic_type.value) ||
+                (isTypeFK(field.semantic_type) &&
                   formField.semantic_type.value === undefined)) && (
                 <Select
                   name={formField.fk_target_field_id.name}
@@ -99,7 +99,7 @@ const Field = ({ field, foreignKeys, url, icon, isEditing, formField }) => (
                   optionValueFn={o => o.id}
                 />
               )
-            : isFK(field.semantic_type) && (
+            : isTypeFK(field.semantic_type) && (
                 <span>
                   {getIn(foreignKeys, [field.fk_target_field_id, "name"])}
                 </span>
diff --git a/frontend/src/metabase/reference/components/FieldTypeDetail.jsx b/frontend/src/metabase/reference/components/FieldTypeDetail.jsx
index a93e7fe651c..774793641a6 100644
--- a/frontend/src/metabase/reference/components/FieldTypeDetail.jsx
+++ b/frontend/src/metabase/reference/components/FieldTypeDetail.jsx
@@ -4,12 +4,11 @@ import cx from "classnames";
 import { getIn } from "icepick";
 import { t } from "ttag";
 import * as MetabaseCore from "metabase/lib/core";
-import { isNumericBaseType } from "metabase/lib/schema_metadata";
 
 import Select from "metabase/core/components/Select";
 
 import D from "metabase/reference/components/Detail.css";
-import { isFK } from "metabase-lib/lib/types/utils/isa";
+import { isTypeFK, isNumericBaseType } from "metabase-lib/lib/types/utils/isa";
 
 const FieldTypeDetail = ({
   field,
@@ -56,8 +55,8 @@ const FieldTypeDetail = ({
         </span>
         <span className="ml4">
           {isEditing
-            ? (isFK(fieldTypeFormField.value) ||
-                (isFK(field.semantic_type) &&
+            ? (isTypeFK(fieldTypeFormField.value) ||
+                (isTypeFK(field.semantic_type) &&
                   fieldTypeFormField.value === undefined)) && (
                 <Select
                   placeholder={t`Select a foreign key`}
@@ -69,7 +68,7 @@ const FieldTypeDetail = ({
                   optionValueFn={o => o.id}
                 />
               )
-            : isFK(field.semantic_type) && (
+            : isTypeFK(field.semantic_type) && (
                 <span>
                   {getIn(foreignKeys, [field.fk_target_field_id, "name"])}
                 </span>
diff --git a/frontend/src/metabase/reference/utils.js b/frontend/src/metabase/reference/utils.js
index 872cdd12a85..2a2c16f8c48 100644
--- a/frontend/src/metabase/reference/utils.js
+++ b/frontend/src/metabase/reference/utils.js
@@ -3,7 +3,7 @@ import { assoc, assocIn, chain } from "icepick";
 import { titleize, humanize } from "metabase/lib/formatting";
 import { startNewCard } from "metabase/lib/card";
 import * as Urls from "metabase/lib/urls";
-import { isPK } from "metabase-lib/lib/types/utils/isa";
+import { isTypePK } from "metabase-lib/lib/types/utils/isa";
 
 export const idsToObjectMap = (ids, objects) =>
   ids
@@ -26,11 +26,12 @@ export const databaseToForeignKeys = database =>
         // ignore tables without primary key
         .filter(
           table =>
-            table && table.fields.find(field => isPK(field.semantic_type)),
+            table && table.fields.find(field => isTypePK(field.semantic_type)),
         )
         .map(table => ({
           table: table,
-          field: table && table.fields.find(field => isPK(field.semantic_type)),
+          field:
+            table && table.fields.find(field => isTypePK(field.semantic_type)),
         }))
         .map(({ table, field }) => ({
           id: field.id,
diff --git a/frontend/src/metabase/visualizations/components/ChoroplethMap.jsx b/frontend/src/metabase/visualizations/components/ChoroplethMap.jsx
index 878c5393461..02914d05118 100644
--- a/frontend/src/metabase/visualizations/components/ChoroplethMap.jsx
+++ b/frontend/src/metabase/visualizations/components/ChoroplethMap.jsx
@@ -7,7 +7,6 @@ import _ from "underscore";
 import Color from "color";
 import LoadingSpinner from "metabase/components/LoadingSpinner";
 
-import { isMetric, isString } from "metabase/lib/schema_metadata";
 import { MinColumnsError } from "metabase/visualizations/lib/errors";
 import MetabaseSettings from "metabase/lib/settings";
 
@@ -17,6 +16,7 @@ import {
   computeMinimalBounds,
   getCanonicalRowKey,
 } from "metabase/visualizations/lib/mapping";
+import { isMetric, isString } from "metabase-lib/lib/types/utils/isa";
 import ChartWithLegend from "./ChartWithLegend";
 import LegacyChoropleth from "./LegacyChoropleth";
 import LeafletChoropleth from "./LeafletChoropleth";
diff --git a/frontend/src/metabase/visualizations/components/LeafletGridHeatMap.jsx b/frontend/src/metabase/visualizations/components/LeafletGridHeatMap.jsx
index 82e9eec26df..517636376ca 100644
--- a/frontend/src/metabase/visualizations/components/LeafletGridHeatMap.jsx
+++ b/frontend/src/metabase/visualizations/components/LeafletGridHeatMap.jsx
@@ -3,7 +3,7 @@ import { t } from "ttag";
 import d3 from "d3";
 
 import { color } from "metabase/lib/colors";
-import { isNumeric, isMetric } from "metabase/lib/schema_metadata";
+import { isNumeric, isMetric } from "metabase-lib/lib/types/utils/isa";
 import { rangeForValue } from "metabase-lib/lib/queries/utils/dataset";
 import { computeNumericDataInverval } from "../lib/numeric";
 import LeafletMap from "./LeafletMap";
diff --git a/frontend/src/metabase/visualizations/components/LeafletMarkerPinMap.jsx b/frontend/src/metabase/visualizations/components/LeafletMarkerPinMap.jsx
index 2ed6a0ead88..748bfe2b511 100644
--- a/frontend/src/metabase/visualizations/components/LeafletMarkerPinMap.jsx
+++ b/frontend/src/metabase/visualizations/components/LeafletMarkerPinMap.jsx
@@ -1,7 +1,7 @@
 import L from "leaflet";
 
 import _ from "underscore";
-import { isPK } from "metabase/lib/schema_metadata";
+import { isPK } from "metabase-lib/lib/types/utils/isa";
 
 import LeafletMap from "./LeafletMap";
 
diff --git a/frontend/src/metabase/visualizations/components/LineAreaBarChart.jsx b/frontend/src/metabase/visualizations/components/LineAreaBarChart.jsx
index 685d34d7225..86f3c32d6ff 100644
--- a/frontend/src/metabase/visualizations/components/LineAreaBarChart.jsx
+++ b/frontend/src/metabase/visualizations/components/LineAreaBarChart.jsx
@@ -9,12 +9,6 @@ import { iconPropTypes } from "metabase/components/Icon";
 
 import "./LineAreaBarChart.css";
 
-import {
-  isNumeric,
-  isDate,
-  isDimension,
-  isMetric,
-} from "metabase/lib/schema_metadata";
 import { getFriendlyName, MAX_SERIES } from "metabase/visualizations/lib/utils";
 import { addCSSRule } from "metabase/lib/dom";
 import { formatValue } from "metabase/lib/formatting";
@@ -25,6 +19,20 @@ import {
   MinRowsError,
   ChartSettingsError,
 } from "metabase/visualizations/lib/errors";
+import { getAccentColors } from "metabase/lib/colors/groups";
+import {
+  isNumeric,
+  isDate,
+  isDimension,
+  isMetric,
+} from "metabase-lib/lib/types/utils/isa";
+
+import {
+  LineAreaBarChartRoot,
+  ChartLegendCaption,
+} from "./LineAreaBarChart.styled";
+import LegendLayout from "./legend/LegendLayout";
+import CardRenderer from "./CardRenderer";
 
 const MUTE_STYLE = "opacity: 0.25;";
 for (let i = 0; i < MAX_SERIES; i++) {
@@ -66,14 +74,6 @@ for (let i = 0; i < MAX_SERIES; i++) {
   addCSSRule(`.LineAreaBarChart.mute-${i} svg:not(.stacked) .row`, MUTE_STYLE);
 }
 
-import { getAccentColors } from "metabase/lib/colors/groups";
-import {
-  LineAreaBarChartRoot,
-  ChartLegendCaption,
-} from "./LineAreaBarChart.styled";
-import LegendLayout from "./legend/LegendLayout";
-import CardRenderer from "./CardRenderer";
-
 export default class LineAreaBarChart extends Component {
   static noHeader = true;
   static supportsSeries = true;
diff --git a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx
index 89bfae20a2f..ee8a5b2251d 100644
--- a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx
+++ b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx
@@ -2,8 +2,6 @@ import React, { useState, useEffect, useCallback } from "react";
 import { connect } from "react-redux";
 import { t } from "ttag";
 
-import { isPK } from "metabase/lib/schema_metadata";
-
 import { State } from "metabase-types/store";
 import type { ForeignKey, ConcreteTableId } from "metabase-types/api";
 import { DatasetData } from "metabase-types/types/Dataset";
@@ -33,6 +31,7 @@ import {
 } from "metabase/query_builder/selectors";
 import { columnSettings } from "metabase/visualizations/lib/settings/column";
 import { isVirtualCardId } from "metabase/lib/saved-questions";
+import { isPK } from "metabase-lib/lib/types/utils/isa";
 import Table from "metabase-lib/lib/metadata/Table";
 import Question from "metabase-lib/lib/Question";
 import { ObjectId, OnVisualizationClickType } from "./types";
diff --git a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetailsTable.tsx b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetailsTable.tsx
index 5d382e6cd47..ecb0ef65e43 100644
--- a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetailsTable.tsx
+++ b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetailsTable.tsx
@@ -7,9 +7,8 @@ import { DatasetData } from "metabase-types/types/Dataset";
 import ExpandableString from "metabase/query_builder/components/ExpandableString";
 import EmptyState from "metabase/components/EmptyState";
 
-import { isID } from "metabase/lib/schema_metadata";
 import { formatValue, formatColumn } from "metabase/lib/formatting";
-import { isa } from "metabase-lib/lib/types/utils/isa";
+import { isa, isID } from "metabase-lib/lib/types/utils/isa";
 import { TYPE } from "metabase-lib/lib/types/constants";
 
 import { OnVisualizationClickType } from "./types";
diff --git a/frontend/src/metabase/visualizations/components/ObjectDetail/utils.ts b/frontend/src/metabase/visualizations/components/ObjectDetail/utils.ts
index 63c7e3d1a29..d2eca12bd59 100644
--- a/frontend/src/metabase/visualizations/components/ObjectDetail/utils.ts
+++ b/frontend/src/metabase/visualizations/components/ObjectDetail/utils.ts
@@ -2,9 +2,9 @@ import { t } from "ttag";
 import _ from "underscore";
 
 import { singularize } from "metabase/lib/formatting";
-import { isPK, isEntityName } from "metabase/lib/schema_metadata";
 
 import { DatasetData, Column } from "metabase-types/types/Dataset";
+import { isPK, isEntityName } from "metabase-lib/lib/types/utils/isa";
 import Question from "metabase-lib/lib/Question";
 import Table from "metabase-lib/lib/metadata/Table";
 
diff --git a/frontend/src/metabase/visualizations/components/PinMap.jsx b/frontend/src/metabase/visualizations/components/PinMap.jsx
index bc6d24ca477..d6cc662ac83 100644
--- a/frontend/src/metabase/visualizations/components/PinMap.jsx
+++ b/frontend/src/metabase/visualizations/components/PinMap.jsx
@@ -5,8 +5,8 @@ import _ from "underscore";
 import cx from "classnames";
 import d3 from "d3";
 import L from "leaflet";
-import { hasLatitudeAndLongitudeColumns } from "metabase/lib/schema_metadata";
 import { LatitudeLongitudeError } from "metabase/visualizations/lib/errors";
+import { hasLatitudeAndLongitudeColumns } from "metabase-lib/lib/types/utils/isa";
 
 import LeafletMarkerPinMap from "./LeafletMarkerPinMap";
 import LeafletTilePinMap from "./LeafletTilePinMap";
diff --git a/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.jsx b/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.jsx
index 66f32fc1fb2..2457eb6bf5a 100644
--- a/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.jsx
+++ b/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.jsx
@@ -17,7 +17,6 @@ import Button from "metabase/core/components/Button";
 import Tooltip from "metabase/components/Tooltip";
 
 import { formatValue } from "metabase/lib/formatting";
-import { isID, isPK, isFK } from "metabase/lib/schema_metadata";
 import {
   getTableCellClickedObject,
   getTableHeaderClickedObject,
@@ -34,6 +33,7 @@ import ExplicitSize from "metabase/components/ExplicitSize";
 
 import Ellipsified from "metabase/core/components/Ellipsified";
 import DimensionInfoPopover from "metabase/components/MetadataInfo/DimensionInfoPopover";
+import { isID, isPK, isFK } from "metabase-lib/lib/types/utils/isa";
 import { fieldRefForColumn } from "metabase-lib/lib/queries/utils/dataset";
 import Dimension from "metabase-lib/lib/Dimension";
 import { memoizeClass } from "metabase-lib/lib/utils";
diff --git a/frontend/src/metabase/visualizations/components/TableSimple/TableCell.jsx b/frontend/src/metabase/visualizations/components/TableSimple/TableCell.jsx
index 4b16ccd1b61..84b17d19670 100644
--- a/frontend/src/metabase/visualizations/components/TableSimple/TableCell.jsx
+++ b/frontend/src/metabase/visualizations/components/TableSimple/TableCell.jsx
@@ -5,13 +5,13 @@ import cx from "classnames";
 import ExternalLink from "metabase/core/components/ExternalLink";
 
 import { formatValue } from "metabase/lib/formatting";
-import { isID, isFK } from "metabase/lib/schema_metadata";
 import {
   getTableCellClickedObject,
   getTableClickedObjectRowData,
   isColumnRightAligned,
 } from "metabase/visualizations/lib/table";
 import { getColumnExtent } from "metabase/visualizations/lib/utils";
+import { isID, isFK } from "metabase-lib/lib/types/utils/isa";
 
 import MiniBar from "../MiniBar";
 import { CellRoot, CellContent } from "./TableCell.styled";
diff --git a/frontend/src/metabase/visualizations/components/TableSimple/TableSimple.jsx b/frontend/src/metabase/visualizations/components/TableSimple/TableSimple.jsx
index 658fe46eef8..a0e99a27820 100644
--- a/frontend/src/metabase/visualizations/components/TableSimple/TableSimple.jsx
+++ b/frontend/src/metabase/visualizations/components/TableSimple/TableSimple.jsx
@@ -13,8 +13,8 @@ import ExplicitSize from "metabase/components/ExplicitSize";
 import Ellipsified from "metabase/core/components/Ellipsified";
 
 import { isPositiveInteger } from "metabase/lib/number";
-import { isID } from "metabase/lib/schema_metadata";
 import { isColumnRightAligned } from "metabase/visualizations/lib/table";
+import { isID } from "metabase-lib/lib/types/utils/isa";
 
 import TableCell from "./TableCell";
 import TableFooter from "./TableFooter";
diff --git a/frontend/src/metabase/visualizations/components/settings/ChartSettingsTableFormatting.jsx b/frontend/src/metabase/visualizations/components/settings/ChartSettingsTableFormatting.jsx
index 00cf6bb71b7..35349814db6 100644
--- a/frontend/src/metabase/visualizations/components/settings/ChartSettingsTableFormatting.jsx
+++ b/frontend/src/metabase/visualizations/components/settings/ChartSettingsTableFormatting.jsx
@@ -26,7 +26,7 @@ import {
 } from "metabase/components/sortable";
 
 import * as MetabaseAnalytics from "metabase/lib/analytics";
-import { isNumeric, isString } from "metabase/lib/schema_metadata";
+import { isNumeric, isString } from "metabase-lib/lib/types/utils/isa";
 
 const NUMBER_OPERATOR_NAMES = {
   "<": t`is less than`,
diff --git a/frontend/src/metabase/visualizations/lib/numeric.js b/frontend/src/metabase/visualizations/lib/numeric.js
index 6d170eb4210..e378177f9ec 100644
--- a/frontend/src/metabase/visualizations/lib/numeric.js
+++ b/frontend/src/metabase/visualizations/lib/numeric.js
@@ -1,4 +1,4 @@
-import { isNumeric } from "metabase/lib/schema_metadata";
+import { isNumeric } from "metabase-lib/lib/types/utils/isa";
 
 export function dimensionIsNumeric({ cols, rows }, i = 0) {
   if (isNumeric(cols[i])) {
diff --git a/frontend/src/metabase/visualizations/lib/settings/column.js b/frontend/src/metabase/visualizations/lib/settings/column.js
index 1965328f1f1..d863644152c 100644
--- a/frontend/src/metabase/visualizations/lib/settings/column.js
+++ b/frontend/src/metabase/visualizations/lib/settings/column.js
@@ -5,14 +5,6 @@ import _ from "underscore";
 
 import ChartNestedSettingColumns from "metabase/visualizations/components/settings/ChartNestedSettingColumns";
 
-import {
-  isDate,
-  isNumber,
-  isCoordinate,
-  isCurrency,
-  isDateWithoutTime,
-} from "metabase/lib/schema_metadata";
-
 // HACK: cyclical dependency causing errors in unit tests
 // import { getVisualizationRaw } from "metabase/visualizations";
 function getVisualizationRaw(...args) {
@@ -51,6 +43,13 @@ export function columnSettings({
 }
 
 import MetabaseSettings from "metabase/lib/settings";
+import {
+  isDate,
+  isNumber,
+  isCoordinate,
+  isCurrency,
+  isDateWithoutTime,
+} from "metabase-lib/lib/types/utils/isa";
 import { keyForColumn } from "metabase-lib/lib/queries/utils/dataset";
 import { nestedSettings } from "./nested";
 
diff --git a/frontend/src/metabase/visualizations/lib/settings/graph.js b/frontend/src/metabase/visualizations/lib/settings/graph.js
index b2f9710c07f..317e9bcc9dc 100644
--- a/frontend/src/metabase/visualizations/lib/settings/graph.js
+++ b/frontend/src/metabase/visualizations/lib/settings/graph.js
@@ -1,11 +1,5 @@
 import { t } from "ttag";
 import _ from "underscore";
-import {
-  isDimension,
-  isMetric,
-  isNumeric,
-  isAny,
-} from "metabase/lib/schema_metadata";
 import {
   columnsAreValid,
   getFriendlyName,
@@ -26,6 +20,12 @@ import { dimensionIsTimeseries } from "metabase/visualizations/lib/timeseries";
 import { getMaxMetricsSupported } from "metabase/visualizations";
 
 import { ChartSettingOrderedSimple } from "metabase/visualizations/components/settings/ChartSettingOrderedSimple";
+import {
+  isDimension,
+  isMetric,
+  isNumeric,
+  isAny,
+} from "metabase-lib/lib/types/utils/isa";
 
 // NOTE: currently we don't consider any date extracts to be histgrams
 const HISTOGRAM_DATE_EXTRACTS = new Set([
diff --git a/frontend/src/metabase/visualizations/lib/settings/utils.js b/frontend/src/metabase/visualizations/lib/settings/utils.js
index 6518e819bd7..95045b1e462 100644
--- a/frontend/src/metabase/visualizations/lib/settings/utils.js
+++ b/frontend/src/metabase/visualizations/lib/settings/utils.js
@@ -1,10 +1,10 @@
 import _ from "underscore";
-import { isDimension, isMetric } from "metabase/lib/schema_metadata";
 import {
   getFriendlyName,
   columnsAreValid,
   getDefaultDimensionAndMetric,
 } from "metabase/visualizations/lib/utils";
+import { isDimension, isMetric } from "metabase-lib/lib/types/utils/isa";
 
 export function getOptionFromColumn(col) {
   return {
diff --git a/frontend/src/metabase/visualizations/lib/table.js b/frontend/src/metabase/visualizations/lib/table.js
index d2aac383166..d0f03a2de8c 100644
--- a/frontend/src/metabase/visualizations/lib/table.js
+++ b/frontend/src/metabase/visualizations/lib/table.js
@@ -1,4 +1,4 @@
-import { isNumber, isCoordinate } from "metabase/lib/schema_metadata";
+import { isNumber, isCoordinate } from "metabase-lib/lib/types/utils/isa";
 
 export function getTableClickedObjectRowData(
   [series],
diff --git a/frontend/src/metabase/visualizations/lib/timeseries.js b/frontend/src/metabase/visualizations/lib/timeseries.js
index 2d4108080a6..5fc0958a48b 100644
--- a/frontend/src/metabase/visualizations/lib/timeseries.js
+++ b/frontend/src/metabase/visualizations/lib/timeseries.js
@@ -1,8 +1,8 @@
 import moment from "moment-timezone";
 import _ from "underscore";
 
-import { isDate } from "metabase/lib/schema_metadata";
 import { parseTimestamp } from "metabase/lib/time";
+import { isDate } from "metabase-lib/lib/types/utils/isa";
 
 import { unexpectedTimezoneWarning, multipleTimezoneWarning } from "./warnings";
 
diff --git a/frontend/src/metabase/visualizations/lib/utils.js b/frontend/src/metabase/visualizations/lib/utils.js
index b96029c5ac1..c947d993353 100644
--- a/frontend/src/metabase/visualizations/lib/utils.js
+++ b/frontend/src/metabase/visualizations/lib/utils.js
@@ -3,7 +3,11 @@ import d3 from "d3";
 import { t } from "ttag";
 import crossfilter from "crossfilter";
 
-import { isDimension, isMetric, isDate } from "metabase/lib/schema_metadata";
+import {
+  isDimension,
+  isMetric,
+  isDate,
+} from "metabase-lib/lib/types/utils/isa";
 
 export const MAX_SERIES = 100;
 
diff --git a/frontend/src/metabase/visualizations/visualizations/Gauge.jsx b/frontend/src/metabase/visualizations/visualizations/Gauge.jsx
index 3eaecc9f00b..3a320d1cb17 100644
--- a/frontend/src/metabase/visualizations/visualizations/Gauge.jsx
+++ b/frontend/src/metabase/visualizations/visualizations/Gauge.jsx
@@ -9,10 +9,10 @@ import _ from "underscore";
 
 import { color } from "metabase/lib/colors";
 import { formatValue } from "metabase/lib/formatting";
-import { isNumeric } from "metabase/lib/schema_metadata";
 import { columnSettings } from "metabase/visualizations/lib/settings/column";
 
 import ChartSettingGaugeSegments from "metabase/visualizations/components/settings/ChartSettingGaugeSegments";
+import { isNumeric } from "metabase-lib/lib/types/utils/isa";
 import { GaugeArcPath } from "./Gauge.styled";
 
 const MAX_WIDTH = 500;
diff --git a/frontend/src/metabase/visualizations/visualizations/List.tsx b/frontend/src/metabase/visualizations/visualizations/List.tsx
index 3326f10922d..3f39ffd090b 100644
--- a/frontend/src/metabase/visualizations/visualizations/List.tsx
+++ b/frontend/src/metabase/visualizations/visualizations/List.tsx
@@ -3,13 +3,6 @@ import { t } from "ttag";
 import _ from "lodash";
 
 import { formatColumn } from "metabase/lib/formatting";
-import {
-  isNumber,
-  isURL,
-  isEmail,
-  isImageURL,
-  isAvatarURL,
-} from "metabase/lib/schema_metadata";
 
 import List from "metabase/visualizations/components/List/List";
 import ChartSettingsListColumns from "metabase/visualizations/components/settings/ChartSettingsListColumns";
@@ -19,6 +12,13 @@ import { VisualizationSettings } from "metabase-types/api/card";
 import { Column } from "metabase-types/types/Dataset";
 import type { DatasetColumn } from "metabase-types/api/dataset";
 import { Series, VisualizationProps } from "metabase-types/types/Visualization";
+import {
+  isNumber,
+  isURL,
+  isEmail,
+  isImageURL,
+  isAvatarURL,
+} from "metabase-lib/lib/types/utils/isa";
 
 function ListViz(props: VisualizationProps) {
   const { data, settings } = props;
diff --git a/frontend/src/metabase/visualizations/visualizations/Map.jsx b/frontend/src/metabase/visualizations/visualizations/Map.jsx
index 19915b52153..0ff79ab7488 100644
--- a/frontend/src/metabase/visualizations/visualizations/Map.jsx
+++ b/frontend/src/metabase/visualizations/visualizations/Map.jsx
@@ -9,15 +9,6 @@ import Link from "metabase/core/components/Link";
 import ExternalLink from "metabase/core/components/ExternalLink";
 import Icon from "metabase/components/Icon";
 
-import {
-  isNumeric,
-  isLatitude,
-  isLongitude,
-  isMetric,
-  hasLatitudeAndLongitudeColumns,
-  isState,
-  isCountry,
-} from "metabase/lib/schema_metadata";
 import { isSameSeries } from "metabase/visualizations/lib/utils";
 import {
   metricSetting,
@@ -33,6 +24,15 @@ const PIN_MAP_TYPES = new Set(["pin", "heat", "grid"]);
 
 import { getAccentColors } from "metabase/lib/colors/groups";
 import ColorRangeSelector from "metabase/core/components/ColorRangeSelector";
+import {
+  isNumeric,
+  isLatitude,
+  isLongitude,
+  isMetric,
+  hasLatitudeAndLongitudeColumns,
+  isState,
+  isCountry,
+} from "metabase-lib/lib/types/utils/isa";
 import LeafletGridHeatMap from "../components/LeafletGridHeatMap";
 import PinMap from "../components/PinMap";
 import ChoroplethMap, {
diff --git a/frontend/src/metabase/visualizations/visualizations/PivotTable.jsx b/frontend/src/metabase/visualizations/visualizations/PivotTable.jsx
index 267563e1958..7f0352f792a 100644
--- a/frontend/src/metabase/visualizations/visualizations/PivotTable.jsx
+++ b/frontend/src/metabase/visualizations/visualizations/PivotTable.jsx
@@ -13,7 +13,6 @@ import ChartSettingsTableFormatting from "metabase/visualizations/components/set
 
 import Ellipsified from "metabase/core/components/Ellipsified";
 import Icon from "metabase/components/Icon";
-import { isDimension } from "metabase/lib/schema_metadata";
 import {
   COLLAPSED_ROWS_SETTING,
   COLUMN_SPLIT_SETTING,
@@ -30,6 +29,7 @@ import { columnSettings } from "metabase/visualizations/lib/settings/column";
 import { ChartSettingIconRadio } from "metabase/visualizations/components/settings/ChartSettingIconRadio";
 
 import { PLUGIN_SELECTORS } from "metabase/plugins";
+import { isDimension } from "metabase-lib/lib/types/utils/isa";
 import {
   PivotTableRoot,
   PivotTableCell,
diff --git a/frontend/src/metabase/visualizations/visualizations/Progress.jsx b/frontend/src/metabase/visualizations/visualizations/Progress.jsx
index e49156b2463..2521b727830 100644
--- a/frontend/src/metabase/visualizations/visualizations/Progress.jsx
+++ b/frontend/src/metabase/visualizations/visualizations/Progress.jsx
@@ -6,12 +6,12 @@ import _ from "underscore";
 import Color from "color";
 import cx from "classnames";
 import { formatValue } from "metabase/lib/formatting";
-import { isNumeric } from "metabase/lib/schema_metadata";
 import Icon from "metabase/components/Icon";
 import IconBorder from "metabase/components/IconBorder";
 import { color } from "metabase/lib/colors";
 
 import { columnSettings } from "metabase/visualizations/lib/settings/column";
+import { isNumeric } from "metabase-lib/lib/types/utils/isa";
 
 const BORDER_RADIUS = 5;
 const MAX_BAR_HEIGHT = 65;
diff --git a/frontend/src/metabase/visualizations/visualizations/SmartScalar.jsx b/frontend/src/metabase/visualizations/visualizations/SmartScalar.jsx
index 5def33591ff..4c33a10894b 100644
--- a/frontend/src/metabase/visualizations/visualizations/SmartScalar.jsx
+++ b/frontend/src/metabase/visualizations/visualizations/SmartScalar.jsx
@@ -3,7 +3,6 @@ import React from "react";
 import { t, jt } from "ttag";
 import _ from "underscore";
 
-import { isDate } from "metabase/lib/schema_metadata";
 import { formatNumber, formatValue } from "metabase/lib/formatting";
 import { color } from "metabase/lib/colors";
 
@@ -16,6 +15,7 @@ import ScalarValue, {
   ScalarWrapper,
   ScalarTitle,
 } from "metabase/visualizations/components/ScalarValue";
+import { isDate } from "metabase-lib/lib/types/utils/isa";
 import { formatBucketing } from "metabase-lib/lib/queries/utils/query-time";
 
 import {
diff --git a/frontend/src/metabase/visualizations/visualizations/Table.jsx b/frontend/src/metabase/visualizations/visualizations/Table.jsx
index 93c2cb6c937..ad7595d3a60 100644
--- a/frontend/src/metabase/visualizations/visualizations/Table.jsx
+++ b/frontend/src/metabase/visualizations/visualizations/Table.jsx
@@ -10,6 +10,14 @@ import { getOptionFromColumn } from "metabase/visualizations/lib/settings/utils"
 import { getColumnCardinality } from "metabase/visualizations/lib/utils";
 import { formatColumn } from "metabase/lib/formatting";
 
+import ChartSettingOrderedColumns from "metabase/visualizations/components/settings/ChartSettingOrderedColumns";
+import ChartSettingsTableFormatting, {
+  isFormattable,
+} from "metabase/visualizations/components/settings/ChartSettingsTableFormatting";
+
+import { makeCellBackgroundGetter } from "metabase/visualizations/lib/table_format";
+import { columnSettings } from "metabase/visualizations/lib/settings/column";
+
 import {
   isMetric,
   isDimension,
@@ -18,15 +26,7 @@ import {
   isEmail,
   isImageURL,
   isAvatarURL,
-} from "metabase/lib/schema_metadata";
-
-import ChartSettingOrderedColumns from "metabase/visualizations/components/settings/ChartSettingOrderedColumns";
-import ChartSettingsTableFormatting, {
-  isFormattable,
-} from "metabase/visualizations/components/settings/ChartSettingsTableFormatting";
-
-import { makeCellBackgroundGetter } from "metabase/visualizations/lib/table_format";
-import { columnSettings } from "metabase/visualizations/lib/settings/column";
+} from "metabase-lib/lib/types/utils/isa";
 import { findColumnIndexForColumnSetting } from "metabase-lib/lib/queries/utils/dataset";
 import * as Q_DEPRECATED from "metabase-lib/lib/queries/utils";
 
diff --git a/frontend/test/metabase/lib/schema_metadata.unit.spec.js b/frontend/test/metabase/lib/schema_metadata.unit.spec.js
index 9f5755f1932..9a3bd65ab96 100644
--- a/frontend/test/metabase/lib/schema_metadata.unit.spec.js
+++ b/frontend/test/metabase/lib/schema_metadata.unit.spec.js
@@ -1,15 +1,5 @@
 import _ from "underscore";
 import {
-  getFieldType,
-  TEMPORAL,
-  STRING,
-  STRING_LIKE,
-  NUMBER,
-  BOOLEAN,
-  LOCATION,
-  COORDINATE,
-  PRIMARY_KEY,
-  FOREIGN_KEY,
   foreignKeyCountsByOriginTable,
   isEqualsOperator,
   doesOperatorExist,
@@ -22,96 +12,15 @@ import {
   getSemanticTypeName,
 } from "metabase/lib/schema_metadata";
 
-import { TYPE } from "metabase-lib/lib/types/constants";
+import {
+  TYPE,
+  NUMBER,
+  COORDINATE,
+  PRIMARY_KEY,
+  FOREIGN_KEY,
+} from "metabase-lib/lib/types/constants";
 
 describe("schema_metadata", () => {
-  describe("getFieldType", () => {
-    it("should know a date", () => {
-      expect(getFieldType({ base_type: TYPE.Date })).toEqual(TEMPORAL);
-      expect(getFieldType({ base_type: TYPE.DateTime })).toEqual(TEMPORAL);
-      expect(getFieldType({ base_type: TYPE.Time })).toEqual(TEMPORAL);
-      expect(getFieldType({ effective_type: TYPE.Date })).toEqual(TEMPORAL);
-      expect(getFieldType({ effective_type: TYPE.DateTime })).toEqual(TEMPORAL);
-      expect(getFieldType({ effective_type: TYPE.Time })).toEqual(TEMPORAL);
-    });
-
-    it("should know a number", () => {
-      expect(getFieldType({ base_type: TYPE.BigInteger })).toEqual(NUMBER);
-      expect(getFieldType({ base_type: TYPE.Integer })).toEqual(NUMBER);
-      expect(getFieldType({ base_type: TYPE.Float })).toEqual(NUMBER);
-      expect(getFieldType({ base_type: TYPE.Decimal })).toEqual(NUMBER);
-    });
-
-    it("should know a string", () => {
-      expect(getFieldType({ base_type: TYPE.Text })).toEqual(STRING);
-    });
-
-    it("should know things that are types of strings", () => {
-      expect(
-        getFieldType({ base_type: TYPE.Text, semantic_type: TYPE.Name }),
-      ).toEqual(STRING);
-      expect(
-        getFieldType({ base_type: TYPE.Text, semantic_type: TYPE.Description }),
-      ).toEqual(STRING);
-      expect(
-        getFieldType({ base_type: TYPE.Text, semantic_type: TYPE.UUID }),
-      ).toEqual(STRING);
-      expect(
-        getFieldType({ base_type: TYPE.Text, semantic_type: TYPE.URL }),
-      ).toEqual(STRING);
-    });
-
-    it("should know a pk", () => {
-      expect(
-        getFieldType({ base_type: TYPE.Integer, semantic_type: TYPE.PK }),
-      ).toEqual(PRIMARY_KEY);
-    });
-
-    it("should know a bool", () => {
-      expect(getFieldType({ base_type: TYPE.Boolean })).toEqual(BOOLEAN);
-    });
-
-    it("should know a location", () => {
-      expect(getFieldType({ semantic_type: TYPE.City })).toEqual(LOCATION);
-      expect(getFieldType({ semantic_type: TYPE.Country })).toEqual(LOCATION);
-    });
-
-    it("should know a coordinate", () => {
-      expect(getFieldType({ semantic_type: TYPE.Latitude })).toEqual(
-        COORDINATE,
-      );
-      expect(getFieldType({ semantic_type: TYPE.Longitude })).toEqual(
-        COORDINATE,
-      );
-    });
-
-    describe("should know something that is string-like", () => {
-      it("TYPE.TextLike", () => {
-        expect(getFieldType({ base_type: TYPE.TextLike })).toEqual(STRING_LIKE);
-      });
-
-      it("TYPE.IPAddress", () => {
-        expect(getFieldType({ base_type: TYPE.IPAddress })).toEqual(
-          STRING_LIKE,
-        );
-      });
-    });
-
-    it("should still recognize some types as a string regardless of its base type", () => {
-      // TYPE.Float can occur in a field filter
-      expect(
-        getFieldType({ base_type: TYPE.Float, semantic_type: TYPE.Name }),
-      ).toEqual(STRING);
-      expect(
-        getFieldType({ base_type: TYPE.Float, semantic_type: TYPE.Category }),
-      ).toEqual(STRING);
-    });
-
-    it("should know what it doesn't know", () => {
-      expect(getFieldType({ base_type: "DERP DERP DERP" })).toEqual(undefined);
-    });
-  });
-
   describe("foreignKeyCountsByOriginTable", () => {
     it("should work with null input", () => {
       expect(foreignKeyCountsByOriginTable(null)).toEqual(null);
-- 
GitLab