diff --git a/.all-contributorsrc b/.all-contributorsrc
index c3b86064..b22c9414 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -1371,6 +1371,16 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "bernardobelchior",
+ "name": "Bernardo Belchior",
+ "avatar_url": "https://avatars.githubusercontent.com/u/12778398?v=4",
+ "profile": "http://belchior.me",
+ "contributions": [
+ "code",
+ "doc"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index aa4eeed7..f239c717 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -55,11 +55,17 @@ jobs:
- name: ⚛️ Setup react
run: npm install react@${{ matrix.react }} react-dom@${{ matrix.react }}
+ - name: ⚛️ Setup react types
+ if: ${{ matrix.react != 'canary' && matrix.react != 'experimental' }}
+ run:
+ npm install @types/react@${{ matrix.react }} @types/react-dom@${{
+ matrix.react }}
+
- name: ▶️ Run validate script
run: npm run validate
- name: ⬆️ Upload coverage report
- uses: codecov/codecov-action@v4
+ uses: codecov/codecov-action@v5
with:
fail_ci_if_error: true
flags: ${{ matrix.react }}
diff --git a/README.md b/README.md
index 85613475..7e18d5dd 100644
--- a/README.md
+++ b/README.md
@@ -97,10 +97,12 @@ primary guiding principle is:
## Installation
This module is distributed via [npm][npm] which is bundled with [node][node] and
-should be installed as one of your project's `devDependencies`:
+should be installed as one of your project's `devDependencies`.
+Starting from RTL version 16, you'll also need to install
+`@testing-library/dom`:
```
-npm install --save-dev @testing-library/react
+npm install --save-dev @testing-library/react @testing-library/dom
```
or
@@ -108,10 +110,11 @@ or
for installation via [yarn][yarn]
```
-yarn add --dev @testing-library/react
+yarn add --dev @testing-library/react @testing-library/dom
```
-This library has `peerDependencies` listings for `react` and `react-dom`.
+This library has `peerDependencies` listings for `react`, `react-dom` and
+starting from RTL version 16 also `@testing-library/dom`.
_React Testing Library versions 13+ require React v18. If your project uses an
older version of React, be sure to install version 12:_
@@ -632,6 +635,7 @@ Thanks goes to these people ([emoji key][emojis]):
 Colin Diesh 📖 |
 Yusuke Iinuma 💻 |
 Jeff Way 💻 |
+  Bernardo Belchior 💻 📖 |
diff --git a/package.json b/package.json
index 3c38a74e..b1bff976 100644
--- a/package.json
+++ b/package.json
@@ -45,25 +45,37 @@
"author": "Kent C. Dodds (https://kentcdodds.com)",
"license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.12.5",
- "@testing-library/dom": "^10.0.0",
- "@types/react-dom": "^18.0.0"
+ "@babel/runtime": "^7.12.5"
},
"devDependencies": {
+ "@testing-library/dom": "^10.0.0",
"@testing-library/jest-dom": "^5.11.6",
+ "@types/react": "^19.0.0",
+ "@types/react-dom": "^19.0.0",
"chalk": "^4.1.2",
"dotenv-cli": "^4.0.0",
"jest-diff": "^29.7.0",
"kcd-scripts": "^13.0.0",
- "npm-run-all": "^4.1.5",
- "react": "^18.3.0",
- "react-dom": "^18.3.0",
+ "npm-run-all2": "^6.2.6",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
"rimraf": "^3.0.2",
"typescript": "^4.1.2"
},
"peerDependencies": {
- "react": "^18.0.0",
- "react-dom": "^18.0.0"
+ "@testing-library/dom": "^10.0.0",
+ "@types/react": "^18.0.0 || ^19.0.0",
+ "@types/react-dom": "^18.0.0 || ^19.0.0",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
},
"eslintConfig": {
"extends": "./node_modules/kcd-scripts/eslint.js",
diff --git a/src/__tests__/error-handlers.js b/src/__tests__/error-handlers.js
new file mode 100644
index 00000000..60db1410
--- /dev/null
+++ b/src/__tests__/error-handlers.js
@@ -0,0 +1,183 @@
+/* eslint-disable jest/no-if */
+/* eslint-disable jest/no-conditional-in-test */
+/* eslint-disable jest/no-conditional-expect */
+import * as React from 'react'
+import {render, renderHook} from '../'
+
+const isReact19 = React.version.startsWith('19.')
+
+const testGateReact19 = isReact19 ? test : test.skip
+
+test('render errors', () => {
+ function Thrower() {
+ throw new Error('Boom!')
+ }
+
+ if (isReact19) {
+ expect(() => {
+ render()
+ }).toThrow('Boom!')
+ } else {
+ expect(() => {
+ expect(() => {
+ render()
+ }).toThrow('Boom!')
+ }).toErrorDev([
+ 'Error: Uncaught [Error: Boom!]',
+ // React retries on error
+ 'Error: Uncaught [Error: Boom!]',
+ ])
+ }
+})
+
+test('onUncaughtError is not supported in render', () => {
+ function Thrower() {
+ throw new Error('Boom!')
+ }
+ const onUncaughtError = jest.fn(() => {})
+
+ expect(() => {
+ render(, {
+ onUncaughtError(error, errorInfo) {
+ console.log({error, errorInfo})
+ },
+ })
+ }).toThrow(
+ 'onUncaughtError is not supported. The `render` call will already throw on uncaught errors.',
+ )
+
+ expect(onUncaughtError).toHaveBeenCalledTimes(0)
+})
+
+testGateReact19('onCaughtError is supported in render', () => {
+ const thrownError = new Error('Boom!')
+ const handleComponentDidCatch = jest.fn()
+ const onCaughtError = jest.fn()
+ class ErrorBoundary extends React.Component {
+ state = {error: null}
+ static getDerivedStateFromError(error) {
+ return {error}
+ }
+ componentDidCatch(error, errorInfo) {
+ handleComponentDidCatch(error, errorInfo)
+ }
+ render() {
+ if (this.state.error) {
+ return null
+ }
+ return this.props.children
+ }
+ }
+ function Thrower() {
+ throw thrownError
+ }
+
+ render(
+
+
+ ,
+ {
+ onCaughtError,
+ },
+ )
+
+ expect(onCaughtError).toHaveBeenCalledWith(thrownError, {
+ componentStack: expect.any(String),
+ errorBoundary: expect.any(Object),
+ })
+})
+
+test('onRecoverableError is supported in render', () => {
+ const onRecoverableError = jest.fn()
+
+ const container = document.createElement('div')
+ container.innerHTML = 'server
'
+ // We just hope we forwarded the callback correctly (which is guaranteed since we just pass it along)
+ // Frankly, I'm too lazy to assert on React 18 hydration errors since they're a mess.
+ // eslint-disable-next-line jest/no-conditional-in-test
+ if (isReact19) {
+ render(client
, {
+ container,
+ hydrate: true,
+ onRecoverableError,
+ })
+ expect(onRecoverableError).toHaveBeenCalledTimes(1)
+ } else {
+ expect(() => {
+ render(client
, {
+ container,
+ hydrate: true,
+ onRecoverableError,
+ })
+ }).toErrorDev(['', ''], {withoutStack: 1})
+ expect(onRecoverableError).toHaveBeenCalledTimes(2)
+ }
+})
+
+test('onUncaughtError is not supported in renderHook', () => {
+ function useThrower() {
+ throw new Error('Boom!')
+ }
+ const onUncaughtError = jest.fn(() => {})
+
+ expect(() => {
+ renderHook(useThrower, {
+ onUncaughtError(error, errorInfo) {
+ console.log({error, errorInfo})
+ },
+ })
+ }).toThrow(
+ 'onUncaughtError is not supported. The `render` call will already throw on uncaught errors.',
+ )
+
+ expect(onUncaughtError).toHaveBeenCalledTimes(0)
+})
+
+testGateReact19('onCaughtError is supported in renderHook', () => {
+ const thrownError = new Error('Boom!')
+ const handleComponentDidCatch = jest.fn()
+ const onCaughtError = jest.fn()
+ class ErrorBoundary extends React.Component {
+ state = {error: null}
+ static getDerivedStateFromError(error) {
+ return {error}
+ }
+ componentDidCatch(error, errorInfo) {
+ handleComponentDidCatch(error, errorInfo)
+ }
+ render() {
+ if (this.state.error) {
+ return null
+ }
+ return this.props.children
+ }
+ }
+ function useThrower() {
+ throw thrownError
+ }
+
+ renderHook(useThrower, {
+ onCaughtError,
+ wrapper: ErrorBoundary,
+ })
+
+ expect(onCaughtError).toHaveBeenCalledWith(thrownError, {
+ componentStack: expect.any(String),
+ errorBoundary: expect.any(Object),
+ })
+})
+
+// Currently, there's no recoverable error without hydration.
+// The option is still supported though.
+test('onRecoverableError is supported in renderHook', () => {
+ const onRecoverableError = jest.fn()
+
+ renderHook(
+ () => {
+ // TODO: trigger recoverable error
+ },
+ {
+ onRecoverableError,
+ },
+ )
+})
diff --git a/src/__tests__/render.js b/src/__tests__/render.js
index f00410b4..6f5b5b39 100644
--- a/src/__tests__/render.js
+++ b/src/__tests__/render.js
@@ -262,4 +262,38 @@ describe('render API', () => {
`\`legacyRoot: true\` is not supported in this version of React. If your app runs React 19 or later, you should remove this flag. If your app runs React 18 or earlier, visit https://react.dev/blog/2022/03/08/react-18-upgrade-guide for upgrade instructions.`,
)
})
+
+ test('reactStrictMode in renderOptions has precedence over config when rendering', () => {
+ const wrapperComponentMountEffect = jest.fn()
+ function WrapperComponent({children}) {
+ React.useEffect(() => {
+ wrapperComponentMountEffect()
+ })
+
+ return children
+ }
+ const ui =
+ configure({reactStrictMode: false})
+
+ render(ui, {wrapper: WrapperComponent, reactStrictMode: true})
+
+ expect(wrapperComponentMountEffect).toHaveBeenCalledTimes(2)
+ })
+
+ test('reactStrictMode in config is used when renderOptions does not specify reactStrictMode', () => {
+ const wrapperComponentMountEffect = jest.fn()
+ function WrapperComponent({children}) {
+ React.useEffect(() => {
+ wrapperComponentMountEffect()
+ })
+
+ return children
+ }
+ const ui =
+ configure({reactStrictMode: true})
+
+ render(ui, {wrapper: WrapperComponent})
+
+ expect(wrapperComponentMountEffect).toHaveBeenCalledTimes(2)
+ })
})
diff --git a/src/__tests__/renderHook.js b/src/__tests__/renderHook.js
index fe7551a2..f331e90e 100644
--- a/src/__tests__/renderHook.js
+++ b/src/__tests__/renderHook.js
@@ -1,5 +1,5 @@
-import React from 'react'
-import {renderHook} from '../pure'
+import React, {useEffect} from 'react'
+import {configure, renderHook} from '../pure'
const isReact18 = React.version.startsWith('18.')
const isReact19 = React.version.startsWith('19.')
@@ -111,3 +111,31 @@ testGateReact19('legacyRoot throws', () => {
`\`legacyRoot: true\` is not supported in this version of React. If your app runs React 19 or later, you should remove this flag. If your app runs React 18 or earlier, visit https://react.dev/blog/2022/03/08/react-18-upgrade-guide for upgrade instructions.`,
)
})
+
+describe('reactStrictMode', () => {
+ let originalConfig
+ beforeEach(() => {
+ // Grab the existing configuration so we can restore
+ // it at the end of the test
+ configure(existingConfig => {
+ originalConfig = existingConfig
+ // Don't change the existing config
+ return {}
+ })
+ })
+
+ afterEach(() => {
+ configure(originalConfig)
+ })
+
+ test('reactStrictMode in renderOptions has precedence over config when rendering', () => {
+ const hookMountEffect = jest.fn()
+ configure({reactStrictMode: false})
+
+ renderHook(() => useEffect(() => hookMountEffect()), {
+ reactStrictMode: true,
+ })
+
+ expect(hookMountEffect).toHaveBeenCalledTimes(2)
+ })
+})
diff --git a/src/pure.js b/src/pure.js
index f546af98..0f9c487d 100644
--- a/src/pure.js
+++ b/src/pure.js
@@ -77,8 +77,8 @@ const mountedContainers = new Set()
*/
const mountedRootEntries = []
-function strictModeIfNeeded(innerElement) {
- return getConfig().reactStrictMode
+function strictModeIfNeeded(innerElement, reactStrictMode) {
+ return reactStrictMode ?? getConfig().reactStrictMode
? React.createElement(React.StrictMode, null, innerElement)
: innerElement
}
@@ -91,18 +91,32 @@ function wrapUiIfNeeded(innerElement, wrapperComponent) {
function createConcurrentRoot(
container,
- {hydrate, ui, wrapper: WrapperComponent},
+ {
+ hydrate,
+ onCaughtError,
+ onRecoverableError,
+ ui,
+ wrapper: WrapperComponent,
+ reactStrictMode,
+ },
) {
let root
if (hydrate) {
act(() => {
root = ReactDOMClient.hydrateRoot(
container,
- strictModeIfNeeded(wrapUiIfNeeded(ui, WrapperComponent)),
+ strictModeIfNeeded(
+ wrapUiIfNeeded(ui, WrapperComponent),
+ reactStrictMode,
+ ),
+ {onCaughtError, onRecoverableError},
)
})
} else {
- root = ReactDOMClient.createRoot(container)
+ root = ReactDOMClient.createRoot(container, {
+ onCaughtError,
+ onRecoverableError,
+ })
}
return {
@@ -140,17 +154,31 @@ function createLegacyRoot(container) {
function renderRoot(
ui,
- {baseElement, container, hydrate, queries, root, wrapper: WrapperComponent},
+ {
+ baseElement,
+ container,
+ hydrate,
+ queries,
+ root,
+ wrapper: WrapperComponent,
+ reactStrictMode,
+ },
) {
act(() => {
if (hydrate) {
root.hydrate(
- strictModeIfNeeded(wrapUiIfNeeded(ui, WrapperComponent)),
+ strictModeIfNeeded(
+ wrapUiIfNeeded(ui, WrapperComponent),
+ reactStrictMode,
+ ),
container,
)
} else {
root.render(
- strictModeIfNeeded(wrapUiIfNeeded(ui, WrapperComponent)),
+ strictModeIfNeeded(
+ wrapUiIfNeeded(ui, WrapperComponent),
+ reactStrictMode,
+ ),
container,
)
}
@@ -176,6 +204,7 @@ function renderRoot(
baseElement,
root,
wrapper: WrapperComponent,
+ reactStrictMode,
})
// Intentionally do not return anything to avoid unnecessarily complicating the API.
// folks can use all the same utilities we return in the first place that are bound to the container
@@ -202,11 +231,20 @@ function render(
container,
baseElement = container,
legacyRoot = false,
+ onCaughtError,
+ onUncaughtError,
+ onRecoverableError,
queries,
hydrate = false,
wrapper,
+ reactStrictMode,
} = {},
) {
+ if (onUncaughtError !== undefined) {
+ throw new Error(
+ 'onUncaughtError is not supported. The `render` call will already throw on uncaught errors.',
+ )
+ }
if (legacyRoot && typeof ReactDOM.render !== 'function') {
const error = new Error(
'`legacyRoot: true` is not supported in this version of React. ' +
@@ -230,7 +268,14 @@ function render(
// eslint-disable-next-line no-negated-condition -- we want to map the evolution of this over time. The root is created first. Only later is it re-used so we don't want to read the case that happens later first.
if (!mountedContainers.has(container)) {
const createRootImpl = legacyRoot ? createLegacyRoot : createConcurrentRoot
- root = createRootImpl(container, {hydrate, ui, wrapper})
+ root = createRootImpl(container, {
+ hydrate,
+ onCaughtError,
+ onRecoverableError,
+ ui,
+ wrapper,
+ reactStrictMode,
+ })
mountedRootEntries.push({container, root})
// we'll add it to the mounted containers regardless of whether it's actually
@@ -255,6 +300,7 @@ function render(
hydrate,
wrapper,
root,
+ reactStrictMode,
})
}
diff --git a/tests/setup-env.js b/tests/setup-env.js
index c9b976f5..1a4401de 100644
--- a/tests/setup-env.js
+++ b/tests/setup-env.js
@@ -1,5 +1,9 @@
import '@testing-library/jest-dom/extend-expect'
import './failOnUnexpectedConsoleCalls'
import {TextEncoder} from 'util'
+import {MessageChannel} from 'worker_threads'
global.TextEncoder = TextEncoder
+// TODO: Revisit once https://github.com/jsdom/jsdom/issues/2448 is resolved
+// This isn't perfect but good enough.
+global.MessageChannel = MessageChannel
diff --git a/tests/toWarnDev.js b/tests/toWarnDev.js
index 2aae39f0..3005125e 100644
--- a/tests/toWarnDev.js
+++ b/tests/toWarnDev.js
@@ -115,7 +115,7 @@ const createMatcherFor = (consoleMethod, matcherName) =>
// doesn't match the number of arguments.
// We'll fail the test if it happens.
let argIndex = 0
- format.replace(/%s/g, () => argIndex++)
+ String(format).replace(/%s/g, () => argIndex++)
if (argIndex !== args.length) {
lastWarningWithMismatchingFormat = {
format,
diff --git a/types/index.d.ts b/types/index.d.ts
index 099bbe84..bdd60567 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -7,7 +7,9 @@ import {
prettyFormat,
Config as ConfigDTL,
} from '@testing-library/dom'
-import {act as reactAct} from 'react-dom/test-utils'
+import {act as reactDeprecatedAct} from 'react-dom/test-utils'
+//@ts-ignore
+import {act as reactAct} from 'react'
export * from '@testing-library/dom'
@@ -34,9 +36,10 @@ export type RenderResult<
baseElement?:
| RendererableContainer
| HydrateableContainer
- | Array,
- maxLength?: number,
- options?: prettyFormat.OptionsReceived,
+ | Array
+ | undefined,
+ maxLength?: number | undefined,
+ options?: prettyFormat.OptionsReceived | undefined,
) => void
rerender: (ui: React.ReactNode) => void
unmount: () => void
@@ -95,40 +98,69 @@ export interface RenderOptions<
*
* @see https://testing-library.com/docs/react-testing-library/api/#container
*/
- container?: Container
+ container?: Container | undefined
/**
* Defaults to the container if the container is specified. Otherwise `document.body` is used for the default. This is used as
* the base element for the queries as well as what is printed when you use `debug()`.
*
* @see https://testing-library.com/docs/react-testing-library/api/#baseelement
*/
- baseElement?: BaseElement
+ baseElement?: BaseElement | undefined
/**
* If `hydrate` is set to `true`, then it will render with `ReactDOM.hydrate`. This may be useful if you are using server-side
* rendering and use ReactDOM.hydrate to mount your components.
*
* @see https://testing-library.com/docs/react-testing-library/api/#hydrate)
*/
- hydrate?: boolean
+ hydrate?: boolean | undefined
/**
* Only works if used with React 18.
* Set to `true` if you want to force synchronous `ReactDOM.render`.
* Otherwise `render` will default to concurrent React if available.
*/
- legacyRoot?: boolean
+ legacyRoot?: boolean | undefined
+ /**
+ * Only supported in React 19.
+ * Callback called when React catches an error in an Error Boundary.
+ * Called with the error caught by the Error Boundary, and an `errorInfo` object containing the `componentStack`.
+ *
+ * @see {@link https://react.dev/reference/react-dom/client/createRoot#parameters createRoot#options}
+ */
+ onCaughtError?: ReactDOMClient.RootOptions extends {
+ onCaughtError: infer OnCaughtError
+ }
+ ? OnCaughtError
+ : never
+ /**
+ * Callback called when React automatically recovers from errors.
+ * Called with an error React throws, and an `errorInfo` object containing the `componentStack`.
+ * Some recoverable errors may include the original error cause as `error.cause`.
+ *
+ * @see {@link https://react.dev/reference/react-dom/client/createRoot#parameters createRoot#options}
+ */
+ onRecoverableError?: ReactDOMClient.RootOptions['onRecoverableError']
+ /**
+ * Not supported at the moment
+ */
+ onUncaughtError?: never
/**
* Queries to bind. Overrides the default set from DOM Testing Library unless merged.
*
* @see https://testing-library.com/docs/react-testing-library/api/#queries
*/
- queries?: Q
+ queries?: Q | undefined
/**
* Pass a React Component as the wrapper option to have it rendered around the inner element. This is most useful for creating
* reusable custom render functions for common data providers. See setup for examples.
*
* @see https://testing-library.com/docs/react-testing-library/api/#wrapper
*/
- wrapper?: React.JSXElementConstructor<{children: React.ReactNode}>
+ wrapper?: React.JSXElementConstructor<{children: React.ReactNode}> | undefined
+ /**
+ * When enabled, is rendered around the inner element.
+ * If defined, overrides the value of `reactStrictMode` set in `configure`.
+ */
+ reactStrictMode?: boolean
}
type Omit = Pick>
@@ -146,7 +178,7 @@ export function render<
): RenderResult
export function render(
ui: React.ReactNode,
- options?: Omit,
+ options?: Omit | undefined,
): RenderResult
export interface RenderHookResult {
@@ -221,7 +253,7 @@ export interface RenderHookOptions<
* The argument passed to the renderHook callback. Can be useful if you plan
* to use the rerender utility to change the values passed to your hook.
*/
- initialProps?: Props
+ initialProps?: Props | undefined
}
/**
@@ -236,7 +268,7 @@ export function renderHook<
BaseElement extends RendererableContainer | HydrateableContainer = Container,
>(
render: (initialProps: Props) => Result,
- options?: RenderHookOptions,
+ options?: RenderHookOptions | undefined,
): RenderHookResult
/**
@@ -245,10 +277,11 @@ export function renderHook<
export function cleanup(): void
/**
- * Simply calls ReactDOMTestUtils.act(cb)
+ * Simply calls React.act(cb)
* If that's not available (older version of react) then it
- * simply calls the given callback immediately
+ * simply calls the deprecated version which is ReactTestUtils.act(cb)
*/
-export const act: typeof reactAct extends undefined
- ? (callback: () => void) => void
+// IfAny from https://stackoverflow.com/a/61626123/3406963
+export const act: 0 extends 1 & typeof reactAct
+ ? typeof reactDeprecatedAct
: typeof reactAct
diff --git a/types/test.tsx b/types/test.tsx
index f8cf4aad..825d5699 100644
--- a/types/test.tsx
+++ b/types/test.tsx
@@ -128,11 +128,20 @@ export function wrappedRender(
ui: React.ReactNode,
options?: pure.RenderOptions,
) {
- const Wrapper = ({children}: {children: React.ReactNode}): JSX.Element => {
+ const Wrapper = ({
+ children,
+ }: {
+ children: React.ReactNode
+ }): React.JSX.Element => {
return {children}
}
- return pure.render(ui, {wrapper: Wrapper, ...options})
+ return pure.render(ui, {
+ wrapper: Wrapper,
+ // testing exactOptionalPropertyTypes comaptibility
+ hydrate: options?.hydrate,
+ ...options,
+ })
}
export function wrappedRenderB(
@@ -254,6 +263,28 @@ export function testContainer() {
renderHook(() => null, {container: document, hydrate: true})
}
+export function testErrorHandlers() {
+ // React 19 types are not used in tests. Verify manually if this works with `"@types/react": "npm:types-react@rc"`
+ render(null, {
+ // Should work with React 19 types
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-expect-error
+ onCaughtError: () => {},
+ })
+ render(null, {
+ // Should never work as it's not supported yet.
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-expect-error
+ onUncaughtError: () => {},
+ })
+ render(null, {
+ onRecoverableError: (error, errorInfo) => {
+ console.error(error)
+ console.log(errorInfo.componentStack)
+ },
+ })
+}
+
/*
eslint
testing-library/prefer-explicit-assert: "off",
diff --git a/types/tsconfig.json b/types/tsconfig.json
index 4e7d649c..bad26af7 100644
--- a/types/tsconfig.json
+++ b/types/tsconfig.json
@@ -1,6 +1,7 @@
{
"extends": "../node_modules/kcd-scripts/shared-tsconfig.json",
"compilerOptions": {
+ "exactOptionalPropertyTypes": true,
"skipLibCheck": false
},
"include": ["."]