diff --git a/README.md b/README.md
index 90e6171c..5cea8e5a 100644
--- a/README.md
+++ b/README.md
@@ -97,6 +97,7 @@ when a real user uses it.
   - [Using other assertion libraries](#using-other-assertion-libraries)
 - [`TextMatch`](#textmatch)
   - [Precision](#precision)
+  - [Normalization](#normalization)
   - [TextMatch Examples](#textmatch-examples)
 - [`query` APIs](#query-apis)
 - [`queryAll` and `getAll` APIs](#queryall-and-getall-apis)
@@ -207,8 +208,7 @@ getByLabelText(
   options?: {
     selector?: string = '*',
     exact?: boolean = true,
-    collapseWhitespace?: boolean = true,
-    trim?: boolean = true,
+    normalizer?: NormalizerFn,
   }): HTMLElement
 ```
 
@@ -259,8 +259,7 @@ getByPlaceholderText(
   text: TextMatch,
   options?: {
     exact?: boolean = true,
-    collapseWhitespace?: boolean = false,
-    trim?: boolean = true,
+    normalizer?: NormalizerFn,
   }): HTMLElement
 ```
 
@@ -284,9 +283,8 @@ getByText(
   options?: {
     selector?: string = '*',
     exact?: boolean = true,
-    collapseWhitespace?: boolean = true,
-    trim?: boolean = true,
-    ignore?: string|boolean = 'script, style'
+    ignore?: string|boolean = 'script, style',
+    normalizer?: NormalizerFn,
   }): HTMLElement
 ```
 
@@ -316,8 +314,7 @@ getByAltText(
   text: TextMatch,
   options?: {
     exact?: boolean = true,
-    collapseWhitespace?: boolean = false,
-    trim?: boolean = true,
+    normalizer?: NormalizerFn,
   }): HTMLElement
 ```
 
@@ -341,8 +338,7 @@ getByTitle(
   title: TextMatch,
   options?: {
     exact?: boolean = true,
-    collapseWhitespace?: boolean = false,
-    trim?: boolean = true,
+    normalizer?: NormalizerFn,
   }): HTMLElement
 ```
 
@@ -368,8 +364,7 @@ getByDisplayValue(
   value: TextMatch,
   options?: {
     exact?: boolean = true,
-    collapseWhitespace?: boolean = false,
-    trim?: boolean = true,
+    normalizer?: NormalizerFn,
   }): HTMLElement
 ```
 
@@ -416,8 +411,7 @@ getByRole(
   text: TextMatch,
   options?: {
     exact?: boolean = true,
-    collapseWhitespace?: boolean = false,
-    trim?: boolean = true,
+    normalizer?: NormalizerFn,
   }): HTMLElement
 ```
 
@@ -437,9 +431,8 @@ getByTestId(
   text: TextMatch,
   options?: {
     exact?: boolean = true,
-    collapseWhitespace?: boolean = false,
-    trim?: boolean = true,
-  }): HTMLElement`
+    normalizer?: NormalizerFn,
+  }): HTMLElement
 ```
 
 A shortcut to `` container.querySelector(`[data-testid="${yourId}"]`) `` (and it
@@ -801,9 +794,47 @@ affect the precision of string matching:
   - `exact` has no effect on `regex` or `function` arguments.
   - In most cases using a regex instead of a string gives you more control over
     fuzzy matching and should be preferred over `{ exact: false }`.
-- `trim`: Defaults to `true`; trim leading and trailing whitespace.
+- `normalizer`: An optional function which overrides normalization behavior.
+  See [`Normalization`](#normalization).
+
+### Normalization
+
+Before running any matching logic against text in the DOM, `dom-testing-library`
+automatically normalizes that text. By default, normalization consists of
+trimming whitespace from the start and end of text, and collapsing multiple
+adjacent whitespace characters into a single space.
+
+If you want to prevent that normalization, or provide alternative
+normalization (e.g. to remove Unicode control characters), you can provide a
+`normalizer` function in the options object. This function will be given
+a string and is expected to return a normalized version of that string.
+
+Note: Specifying a value for `normalizer` _replaces_ the built-in normalization, but
+you can call `getDefaultNormalizer` to obtain a built-in normalizer, either
+to adjust that normalization or to call it from your own normalizer.
+
+`getDefaultNormalizer` takes an options object which allows the selection of behaviour:
+
+- `trim`: Defaults to `true`. Trims leading and trailing whitespace
 - `collapseWhitespace`: Defaults to `true`. Collapses inner whitespace (newlines, tabs, repeated spaces) into a single space.
 
+#### Normalization Examples
+
+To perform a match against text without trimming:
+
+```javascript
+getByText(node, 'text', {normalizer: getDefaultNormalizer({trim: false})})
+```
+
+To override normalization to remove some Unicode characters whilst keeping some (but not all) of the built-in normalization behavior:
+
+```javascript
+getByText(node, 'text', {
+  normalizer: str =>
+    getDefaultNormalizer({trim: false})(str).replace(/[\u200E-\u200F]*/g, ''),
+})
+```
+
 ### TextMatch Examples
 
 ```javascript
diff --git a/src/__tests__/matches.js b/src/__tests__/matches.js
index 8296a54f..5e4d7243 100644
--- a/src/__tests__/matches.js
+++ b/src/__tests__/matches.js
@@ -1,25 +1,28 @@
-import {fuzzyMatches, matches} from '../'
+import {fuzzyMatches, matches} from '../matches'
 
 // unit tests for text match utils
 
 const node = null
+const normalizer = str => str
 
 test('matchers accept strings', () => {
-  expect(matches('ABC', node, 'ABC')).toBe(true)
-  expect(fuzzyMatches('ABC', node, 'ABC')).toBe(true)
+  expect(matches('ABC', node, 'ABC', normalizer)).toBe(true)
+  expect(fuzzyMatches('ABC', node, 'ABC', normalizer)).toBe(true)
 })
 
 test('matchers accept regex', () => {
-  expect(matches('ABC', node, /ABC/)).toBe(true)
-  expect(fuzzyMatches('ABC', node, /ABC/)).toBe(true)
+  expect(matches('ABC', node, /ABC/, normalizer)).toBe(true)
+  expect(fuzzyMatches('ABC', node, /ABC/, normalizer)).toBe(true)
 })
 
 test('matchers accept functions', () => {
-  expect(matches('ABC', node, text => text === 'ABC')).toBe(true)
-  expect(fuzzyMatches('ABC', node, text => text === 'ABC')).toBe(true)
+  expect(matches('ABC', node, text => text === 'ABC', normalizer)).toBe(true)
+  expect(fuzzyMatches('ABC', node, text => text === 'ABC', normalizer)).toBe(
+    true,
+  )
 })
 
 test('matchers return false if text to match is not a string', () => {
-  expect(matches(null, node, 'ABC')).toBe(false)
-  expect(fuzzyMatches(null, node, 'ABC')).toBe(false)
+  expect(matches(null, node, 'ABC', normalizer)).toBe(false)
+  expect(fuzzyMatches(null, node, 'ABC', normalizer)).toBe(false)
 })
diff --git a/src/__tests__/text-matchers.js b/src/__tests__/text-matchers.js
index 5cf7b359..7cbd6924 100644
--- a/src/__tests__/text-matchers.js
+++ b/src/__tests__/text-matchers.js
@@ -1,5 +1,7 @@
 import 'jest-dom/extend-expect'
 import cases from 'jest-in-case'
+
+import {getDefaultNormalizer} from '../'
 import {render} from './helpers/test-utils'
 
 cases(
@@ -68,7 +70,12 @@ cases(
     const queries = render(dom)
     expect(queries[queryFn](query)).toHaveLength(1)
     expect(
-      queries[queryFn](query, {collapseWhitespace: false, trim: false}),
+      queries[queryFn](query, {
+        normalizer: getDefaultNormalizer({
+          collapseWhitespace: false,
+          trim: false,
+        }),
+      }),
     ).toHaveLength(0)
   },
   {
@@ -194,3 +201,128 @@ cases(
     },
   },
 )
+
+// A good use case for a custom normalizer is stripping
+// out Unicode control characters such as LRM (left-right-mark)
+// before matching
+const LRM = '\u200e'
+function removeUCC(str) {
+  return str.replace(/[\u200e]/g, '')
+}
+
+cases(
+  '{ normalizer } option allows custom pre-match normalization',
+  ({dom, queryFn}) => {
+    const queries = render(dom)
+
+    const query = queries[queryFn]
+
+    // With the correct normalizer, we should match
+    expect(query(/user n.me/i, {normalizer: removeUCC})).toHaveLength(1)
+    expect(query('User name', {normalizer: removeUCC})).toHaveLength(1)
+
+    // Without the normalizer, we shouldn't
+    expect(query(/user n.me/i)).toHaveLength(0)
+    expect(query('User name')).toHaveLength(0)
+  },
+  {
+    queryAllByLabelText: {
+      dom: `
+        <label for="username">User ${LRM}name</label>
+        <input id="username" />`,
+      queryFn: 'queryAllByLabelText',
+    },
+    queryAllByPlaceholderText: {
+      dom: `<input placeholder="User ${LRM}name" />`,
+      queryFn: 'queryAllByPlaceholderText',
+    },
+    queryAllBySelectText: {
+      dom: `<select><option>User ${LRM}name</option></select>`,
+      queryFn: 'queryAllBySelectText',
+    },
+    queryAllByText: {
+      dom: `<div>User ${LRM}name</div>`,
+      queryFn: 'queryAllByText',
+    },
+    queryAllByAltText: {
+      dom: `<img alt="User ${LRM}name" src="username.jpg" />`,
+      queryFn: 'queryAllByAltText',
+    },
+    queryAllByTitle: {
+      dom: `<div title="User ${LRM}name" />`,
+      queryFn: 'queryAllByTitle',
+    },
+    queryAllByValue: {
+      dom: `<input value="User ${LRM}name" />`,
+      queryFn: 'queryAllByValue',
+    },
+    queryAllByDisplayValue: {
+      dom: `<input value="User ${LRM}name" />`,
+      queryFn: 'queryAllByDisplayValue',
+    },
+    queryAllByRole: {
+      dom: `<input role="User ${LRM}name" />`,
+      queryFn: 'queryAllByRole',
+    },
+  },
+)
+
+test('normalizer works with both exact and non-exact matching', () => {
+  const {queryAllByText} = render(`<div>MiXeD ${LRM}CaSe</div>`)
+
+  expect(
+    queryAllByText('mixed case', {exact: false, normalizer: removeUCC}),
+  ).toHaveLength(1)
+  expect(
+    queryAllByText('mixed case', {exact: true, normalizer: removeUCC}),
+  ).toHaveLength(0)
+  expect(
+    queryAllByText('MiXeD CaSe', {exact: true, normalizer: removeUCC}),
+  ).toHaveLength(1)
+  expect(queryAllByText('MiXeD CaSe', {exact: true})).toHaveLength(0)
+})
+
+test('top-level trim and collapseWhitespace options are not supported if normalizer is specified', () => {
+  const {queryAllByText} = render('<div>  abc  def  </div>')
+  const normalizer = str => str
+
+  expect(() => queryAllByText('abc', {trim: false, normalizer})).toThrow()
+  expect(() => queryAllByText('abc', {trim: true, normalizer})).toThrow()
+  expect(() =>
+    queryAllByText('abc', {collapseWhitespace: false, normalizer}),
+  ).toThrow()
+  expect(() =>
+    queryAllByText('abc', {collapseWhitespace: true, normalizer}),
+  ).toThrow()
+})
+
+test('getDefaultNormalizer returns a normalizer that supports trim and collapseWhitespace', () => {
+  // Default is trim: true and collapseWhitespace: true
+  expect(getDefaultNormalizer()('  abc  def  ')).toEqual('abc def')
+
+  // Turning off trimming should not turn off whitespace collapsing
+  expect(getDefaultNormalizer({trim: false})('  abc  def  ')).toEqual(
+    ' abc def ',
+  )
+
+  // Turning off whitespace collapsing should not turn off trimming
+  expect(
+    getDefaultNormalizer({collapseWhitespace: false})('  abc  def  '),
+  ).toEqual('abc  def')
+
+  // Whilst it's rather pointless, we should be able to turn both off
+  expect(
+    getDefaultNormalizer({trim: false, collapseWhitespace: false})(
+      '  abc  def  ',
+    ),
+  ).toEqual('  abc  def  ')
+})
+
+test('we support an older API with trim and collapseWhitespace instead of a normalizer', () => {
+  const {queryAllByText} = render('<div>  x  y  </div>')
+  expect(queryAllByText('x y')).toHaveLength(1)
+  expect(queryAllByText('x y', {trim: false})).toHaveLength(0)
+  expect(queryAllByText(' x y ', {trim: false})).toHaveLength(1)
+  expect(queryAllByText('x y', {collapseWhitespace: false})).toHaveLength(0)
+  expect(queryAllByText('x  y', {collapseWhitespace: false})).toHaveLength(1)
+})
diff --git a/src/index.js b/src/index.js
index 5b9dbefa..c69df8a5 100644
--- a/src/index.js
+++ b/src/index.js
@@ -6,7 +6,7 @@ export * from './queries'
 export * from './wait'
 export * from './wait-for-element'
 export * from './wait-for-dom-change'
-export * from './matches'
+export {getDefaultNormalizer} from './matches'
 export * from './get-node-text'
 export * from './events'
 export * from './get-queries-for-element'
diff --git a/src/matches.js b/src/matches.js
index 1a79cb4f..3241e680 100644
--- a/src/matches.js
+++ b/src/matches.js
@@ -1,13 +1,9 @@
-function fuzzyMatches(
-  textToMatch,
-  node,
-  matcher,
-  {collapseWhitespace = true, trim = true} = {},
-) {
+function fuzzyMatches(textToMatch, node, matcher, normalizer) {
   if (typeof textToMatch !== 'string') {
     return false
   }
-  const normalizedText = normalize(textToMatch, {trim, collapseWhitespace})
+
+  const normalizedText = normalizer(textToMatch)
   if (typeof matcher === 'string') {
     return normalizedText.toLowerCase().includes(matcher.toLowerCase())
   } else if (typeof matcher === 'function') {
@@ -17,16 +13,12 @@ function fuzzyMatches(
   }
 }
 
-function matches(
-  textToMatch,
-  node,
-  matcher,
-  {collapseWhitespace = true, trim = true} = {},
-) {
+function matches(textToMatch, node, matcher, normalizer) {
   if (typeof textToMatch !== 'string') {
     return false
   }
-  const normalizedText = normalize(textToMatch, {trim, collapseWhitespace})
+
+  const normalizedText = normalizer(textToMatch)
   if (typeof matcher === 'string') {
     return normalizedText === matcher
   } else if (typeof matcher === 'function') {
@@ -36,13 +28,46 @@ function matches(
   }
 }
 
-function normalize(text, {trim, collapseWhitespace}) {
-  let normalizedText = text
-  normalizedText = trim ? normalizedText.trim() : normalizedText
-  normalizedText = collapseWhitespace
-    ? normalizedText.replace(/\s+/g, ' ')
-    : normalizedText
-  return normalizedText
+function getDefaultNormalizer({trim = true, collapseWhitespace = true} = {}) {
+  return text => {
+    let normalizedText = text
+    normalizedText = trim ? normalizedText.trim() : normalizedText
+    normalizedText = collapseWhitespace
+      ? normalizedText.replace(/\s+/g, ' ')
+      : normalizedText
+    return normalizedText
+  }
+}
+
+/**
+ * Constructs a normalizer to pass to functions in matches.js
+ * @param {boolean|undefined} trim The user-specified value for `trim`, without
+ * any defaulting having been applied
+ * @param {boolean|undefined} collapseWhitespace The user-specified value for
+ * `collapseWhitespace`, without any defaulting having been applied
+ * @param {Function|undefined} normalizer The user-specified normalizer
+ * @returns {Function} A normalizer
+ */
+function makeNormalizer({trim, collapseWhitespace, normalizer}) {
+  if (normalizer) {
+    // User has specified a custom normalizer
+    if (
+      typeof trim !== 'undefined' ||
+      typeof collapseWhitespace !== 'undefined'
+    ) {
+      // They've also specified a value for trim or collapseWhitespace
+      throw new Error(
+        'trim and collapseWhitespace are not supported with a normalizer. ' +
+          'If you want to use the default trim and collapseWhitespace logic in your normalizer, ' +
+          'use "getDefaultNormalizer({trim, collapseWhitespace})" and compose that into your normalizer',
+      )
+    }
+
+    return normalizer
+  } else {
+    // No custom normalizer specified. Just use default.
+    return getDefaultNormalizer({trim, collapseWhitespace})
+  }
 }
 
-export {fuzzyMatches, matches}
+export {fuzzyMatches, matches, getDefaultNormalizer, makeNormalizer}
diff --git a/src/queries.js b/src/queries.js
index d9bf7c7d..e5393222 100644
--- a/src/queries.js
+++ b/src/queries.js
@@ -1,4 +1,4 @@
-import {fuzzyMatches, matches} from './matches'
+import {fuzzyMatches, matches, makeNormalizer} from './matches'
 import {getNodeText} from './get-node-text'
 import {
   getElementError,
@@ -15,22 +15,25 @@ import {getConfig} from './config'
 function queryAllLabelsByText(
   container,
   text,
-  {exact = true, trim = true, collapseWhitespace = true} = {},
+  {exact = true, trim, collapseWhitespace, normalizer} = {},
 ) {
   const matcher = exact ? matches : fuzzyMatches
-  const matchOpts = {collapseWhitespace, trim}
+  const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
   return Array.from(container.querySelectorAll('label')).filter(label =>
-    matcher(label.textContent, label, text, matchOpts),
+    matcher(label.textContent, label, text, matchNormalizer),
   )
 }
 
 function queryAllByLabelText(
   container,
   text,
-  {selector = '*', exact = true, collapseWhitespace = true, trim = true} = {},
+  {selector = '*', exact = true, collapseWhitespace, trim, normalizer} = {},
 ) {
-  const matchOpts = {collapseWhitespace, trim}
-  const labels = queryAllLabelsByText(container, text, {exact, ...matchOpts})
+  const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
+  const labels = queryAllLabelsByText(container, text, {
+    exact,
+    normalizer: matchNormalizer,
+  })
   const labelledElements = labels
     .map(label => {
       if (label.control) {
@@ -62,7 +65,7 @@ function queryAllByLabelText(
 
   const possibleAriaLabelElements = queryAllByText(container, text, {
     exact,
-    ...matchOpts,
+    normalizer: matchNormalizer,
   }).filter(el => el.tagName !== 'LABEL') // don't reprocess labels
 
   const ariaLabelledElements = possibleAriaLabelElements.reduce(
@@ -94,16 +97,17 @@ function queryAllByText(
   {
     selector = '*',
     exact = true,
-    collapseWhitespace = true,
-    trim = true,
+    collapseWhitespace,
+    trim,
     ignore = 'script, style',
+    normalizer,
   } = {},
 ) {
   const matcher = exact ? matches : fuzzyMatches
-  const matchOpts = {collapseWhitespace, trim}
+  const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
   return Array.from(container.querySelectorAll(selector))
     .filter(node => !ignore || !node.matches(ignore))
-    .filter(node => matcher(getNodeText(node), node, text, matchOpts))
+    .filter(node => matcher(getNodeText(node), node, text, matchNormalizer))
 }
 
 function queryByText(...args) {
@@ -113,14 +117,14 @@ function queryByText(...args) {
 function queryAllByTitle(
   container,
   text,
-  {exact = true, collapseWhitespace = true, trim = true} = {},
+  {exact = true, collapseWhitespace, trim, normalizer} = {},
 ) {
   const matcher = exact ? matches : fuzzyMatches
-  const matchOpts = {collapseWhitespace, trim}
+  const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
   return Array.from(container.querySelectorAll('[title], svg > title')).filter(
     node =>
-      matcher(node.getAttribute('title'), node, text, matchOpts) ||
-      matcher(getNodeText(node), node, text, matchOpts),
+      matcher(node.getAttribute('title'), node, text, matchNormalizer) ||
+      matcher(getNodeText(node), node, text, matchNormalizer),
   )
 }
 
@@ -131,16 +135,16 @@ function queryByTitle(...args) {
 function queryAllBySelectText(
   container,
   text,
-  {exact = true, collapseWhitespace = true, trim = true} = {},
+  {exact = true, collapseWhitespace, trim, normalizer} = {},
 ) {
   const matcher = exact ? matches : fuzzyMatches
-  const matchOpts = {collapseWhitespace, trim}
+  const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
   return Array.from(container.querySelectorAll('select')).filter(selectNode => {
     const selectedOptions = Array.from(selectNode.options).filter(
       option => option.selected,
     )
     return selectedOptions.some(optionNode =>
-      matcher(getNodeText(optionNode), optionNode, text, matchOpts),
+      matcher(getNodeText(optionNode), optionNode, text, matchNormalizer),
     )
   })
 }
@@ -167,12 +171,12 @@ const queryAllByRole = queryAllByAttribute.bind(null, 'role')
 function queryAllByAltText(
   container,
   alt,
-  {exact = true, collapseWhitespace = true, trim = true} = {},
+  {exact = true, collapseWhitespace, trim, normalizer} = {},
 ) {
   const matcher = exact ? matches : fuzzyMatches
-  const matchOpts = {collapseWhitespace, trim}
+  const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
   return Array.from(container.querySelectorAll('img,input,area')).filter(node =>
-    matcher(node.getAttribute('alt'), node, alt, matchOpts),
+    matcher(node.getAttribute('alt'), node, alt, matchNormalizer),
   )
 }
 
@@ -183,10 +187,10 @@ function queryByAltText(...args) {
 function queryAllByDisplayValue(
   container,
   value,
-  {exact = true, collapseWhitespace = true, trim = true} = {},
+  {exact = true, collapseWhitespace, trim, normalizer} = {},
 ) {
   const matcher = exact ? matches : fuzzyMatches
-  const matchOpts = {collapseWhitespace, trim}
+  const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
   return Array.from(container.querySelectorAll(`input,textarea,select`)).filter(
     node => {
       if (node.tagName === 'SELECT') {
@@ -194,10 +198,10 @@ function queryAllByDisplayValue(
           option => option.selected,
         )
         return selectedOptions.some(optionNode =>
-          matcher(getNodeText(optionNode), optionNode, value, matchOpts),
+          matcher(getNodeText(optionNode), optionNode, value, matchNormalizer),
         )
       } else {
-        return matcher(node.value, node, value, matchOpts)
+        return matcher(node.value, node, value, matchNormalizer)
       }
     },
   )
diff --git a/src/query-helpers.js b/src/query-helpers.js
index 1f701f38..8f849f47 100644
--- a/src/query-helpers.js
+++ b/src/query-helpers.js
@@ -1,5 +1,5 @@
 import {prettyDOM} from './pretty-dom'
-import {fuzzyMatches, matches} from './matches'
+import {fuzzyMatches, matches, makeNormalizer} from './matches'
 
 /* eslint-disable complexity */
 function debugDOM(htmlElement) {
@@ -39,12 +39,12 @@ function queryAllByAttribute(
   attribute,
   container,
   text,
-  {exact = true, collapseWhitespace = true, trim = true} = {},
+  {exact = true, collapseWhitespace, trim, normalizer} = {},
 ) {
   const matcher = exact ? matches : fuzzyMatches
-  const matchOpts = {collapseWhitespace, trim}
+  const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
   return Array.from(container.querySelectorAll(`[${attribute}]`)).filter(node =>
-    matcher(node.getAttribute(attribute), node, text, matchOpts),
+    matcher(node.getAttribute(attribute), node, text, matchNormalizer),
   )
 }
 
diff --git a/typings/matches.d.ts b/typings/matches.d.ts
index af89af82..39e7b643 100644
--- a/typings/matches.d.ts
+++ b/typings/matches.d.ts
@@ -1,9 +1,15 @@
 export type MatcherFunction = (content: string, element: HTMLElement) => boolean
 export type Matcher = string | RegExp | MatcherFunction
+
+export type NormalizerFn = (text: string) => string
+
 export interface MatcherOptions {
   exact?: boolean
+  /** Use normalizer with getDefaultNormalizer instead */
   trim?: boolean
+  /** Use normalizer with getDefaultNormalizer instead */
   collapseWhitespace?: boolean
+  normalizer?: NormalizerFn
 }
 
 export type Match = (
@@ -13,5 +19,13 @@ export type Match = (
   options?: MatcherOptions,
 ) => boolean
 
-export const fuzzyMatches: Match
-export const matches: Match
+export interface DefaultNormalizerOptions {
+  trim?: boolean
+  collapseWhitespace?: boolean
+}
+
+export declare function getDefaultNormalizer(
+  options?: DefaultNormalizerOptions,
+): NormalizerFn
+
+// N.B. Don't expose fuzzyMatches + matches here: they're not public API
diff --git a/typings/queries.d.ts b/typings/queries.d.ts
index 027169d4..42b45da7 100644
--- a/typings/queries.d.ts
+++ b/typings/queries.d.ts
@@ -1,7 +1,5 @@
 import {Matcher, MatcherOptions} from './matches'
-import {
-  SelectorMatcherOptions,
-} from './query-helpers'
+import {SelectorMatcherOptions} from './query-helpers'
 
 export type QueryByBoundAttribute = (
   container: HTMLElement,