diff --git a/package.json b/package.json
index 73095fc3..409328bd 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,7 @@
   "version": "0.0.0-semantically-released",
   "description": "Simple and complete DOM testing utilities that encourage good testing practices.",
   "main": "dist/index.js",
+  "types": "types/index.d.ts",
   "module": "dist/@testing-library/dom.esm.js",
   "umd:main": "dist/@testing-library/dom.umd.js",
   "source": "src/index.js",
@@ -29,19 +30,21 @@
     "test": "kcd-scripts test",
     "test:debug": "node --inspect-brk ./node_modules/.bin/jest --watch --runInBand",
     "test:update": "npm test -- --updateSnapshot --coverage",
-    "validate": "kcd-scripts validate"
+    "validate": "kcd-scripts validate",
+    "typecheck": "dtslint ./types/"
   },
   "files": [
-    "dist"
+    "dist",
+    "types"
   ],
   "dependencies": {
     "@babel/runtime": "^7.9.6",
-    "@types/testing-library__dom": "^7.0.2",
     "aria-query": "^4.0.2",
     "dom-accessibility-api": "^0.4.3",
     "pretty-format": "^26.0.1"
   },
   "devDependencies": {
+    "dtslint": "^3.4.2",
     "@testing-library/jest-dom": "^5.5.0",
     "jest-in-case": "^1.0.2",
     "jest-serializer-ansi": "^1.0.3",
diff --git a/types/__tests__/type-tests.ts b/types/__tests__/type-tests.ts
new file mode 100644
index 00000000..5203434e
--- /dev/null
+++ b/types/__tests__/type-tests.ts
@@ -0,0 +1,122 @@
+import {
+  fireEvent,
+  isInaccessible,
+  queries,
+  screen,
+  waitFor,
+  waitForElementToBeRemoved,
+} from '../index'
+
+const {
+  getByText,
+  queryByText,
+  findByText,
+  getAllByText,
+  queryAllByText,
+  findAllByText,
+  queryAllByRole,
+  queryByRole,
+  findByRole,
+} = queries
+
+async function testQueries() {
+  // element queries
+  const element = document.createElement('div')
+  getByText(element, 'foo')
+  queryByText(element, 'foo')
+  await findByText(element, 'foo')
+  await findByText(element, 'foo', undefined, {timeout: 10})
+  getAllByText(element, 'bar')
+  queryAllByText(element, 'bar')
+  await findAllByText(element, 'bar')
+  await findAllByText(element, 'bar', undefined, {timeout: 10})
+
+  // screen queries
+  screen.getByText('foo')
+  screen.queryByText('foo')
+  await screen.findByText('foo')
+  await screen.findByText('foo', undefined, {timeout: 10})
+  screen.debug(screen.getAllByText('bar'))
+  screen.queryAllByText('bar')
+  await screen.findAllByText('bar')
+  await screen.findAllByText('bar', undefined, {timeout: 10})
+}
+
+async function testByRole() {
+  const element = document.createElement('button')
+  element.setAttribute('aria-hidden', 'true')
+
+  console.assert(queryByRole(element, 'button') === null)
+  console.assert(queryByRole(element, 'button', {hidden: true}) !== null)
+
+  console.assert(screen.queryByRole('button') === null)
+  console.assert(screen.queryByRole('button', {hidden: true}) !== null)
+
+  console.assert(
+    (await findByRole(element, 'button', undefined, {timeout: 10})) === null,
+  )
+  console.assert(
+    (await findByRole(element, 'button', {hidden: true}, {timeout: 10})) !==
+      null,
+  )
+
+  console.assert(
+    queryAllByRole(document.body, 'progressbar', {queryFallbacks: true})
+      .length === 1,
+  )
+
+  // `name` option
+  console.assert(queryByRole(element, 'button', {name: 'Logout'}) === null)
+  console.assert(queryByRole(element, 'button', {name: /^Log/}) === null)
+  console.assert(
+    queryByRole(element, 'button', {
+      name: (name, element) =>
+        name === 'Login' && element.hasAttribute('disabled'),
+    }) === null,
+  )
+}
+
+function testA11yHelper() {
+  const element = document.createElement('svg')
+  console.assert(!isInaccessible(element))
+}
+
+function eventTest() {
+  fireEvent.popState(window, {
+    location: 'http://www.example.com/?page=1',
+    state: {page: 1},
+  })
+
+  // HTMLElement
+  const element = document.createElement('div')
+  fireEvent.click(getByText(element, 'foo'))
+
+  // ChildNode
+  const child = document.createElement('div')
+  element.appendChild(child)
+  if (!element.firstChild) {
+    // Narrow Type
+    throw new Error(`Can't find firstChild`)
+  }
+  fireEvent.click(element.firstChild)
+}
+
+async function testWaitFors() {
+  const element = document.createElement('div')
+
+  await waitFor(() => getByText(element, 'apple'))
+  await waitFor(() => getAllByText(element, 'apple'))
+  const result: HTMLSpanElement = await waitFor(() =>
+    getByText(element, 'apple'),
+  )
+  if (!result) {
+    // Use value
+    throw new Error(`Can't find result`)
+  }
+
+  element.innerHTML = '<span>apple</span>'
+
+  await waitForElementToBeRemoved(() => getByText(element, 'apple'), {interval: 3000, container: element, timeout: 5000})
+  await waitForElementToBeRemoved(getByText(element, 'apple'))
+  await waitForElementToBeRemoved(getAllByText(element, 'apple'))
+}
diff --git a/types/config.d.ts b/types/config.d.ts
new file mode 100644
index 00000000..a2063d99
--- /dev/null
+++ b/types/config.d.ts
@@ -0,0 +1,12 @@
+export interface Config {
+    testIdAttribute: string;
+    asyncWrapper(cb: (...args: any[]) => any): Promise<any>;
+    asyncUtilTimeout: number;
+    defaultHidden: boolean;
+}
+
+export interface ConfigFn {
+    (existingConfig: Config): Partial<Config>;
+}
+
+export function configure(configDelta: Partial<Config> | ConfigFn): void;
diff --git a/types/events.d.ts b/types/events.d.ts
new file mode 100644
index 00000000..d9c50cb7
--- /dev/null
+++ b/types/events.d.ts
@@ -0,0 +1,95 @@
+export type EventType =
+    | 'copy'
+    | 'cut'
+    | 'paste'
+    | 'compositionEnd'
+    | 'compositionStart'
+    | 'compositionUpdate'
+    | 'keyDown'
+    | 'keyPress'
+    | 'keyUp'
+    | 'focus'
+    | 'blur'
+    | 'focusIn'
+    | 'focusOut'
+    | 'change'
+    | 'input'
+    | 'invalid'
+    | 'submit'
+    | 'reset'
+    | 'click'
+    | 'contextMenu'
+    | 'dblClick'
+    | 'drag'
+    | 'dragEnd'
+    | 'dragEnter'
+    | 'dragExit'
+    | 'dragLeave'
+    | 'dragOver'
+    | 'dragStart'
+    | 'drop'
+    | 'mouseDown'
+    | 'mouseEnter'
+    | 'mouseLeave'
+    | 'mouseMove'
+    | 'mouseOut'
+    | 'mouseOver'
+    | 'mouseUp'
+    | 'popState'
+    | 'select'
+    | 'touchCancel'
+    | 'touchEnd'
+    | 'touchMove'
+    | 'touchStart'
+    | 'scroll'
+    | 'wheel'
+    | 'abort'
+    | 'canPlay'
+    | 'canPlayThrough'
+    | 'durationChange'
+    | 'emptied'
+    | 'encrypted'
+    | 'ended'
+    | 'loadedData'
+    | 'loadedMetadata'
+    | 'loadStart'
+    | 'pause'
+    | 'play'
+    | 'playing'
+    | 'progress'
+    | 'rateChange'
+    | 'seeked'
+    | 'seeking'
+    | 'stalled'
+    | 'suspend'
+    | 'timeUpdate'
+    | 'volumeChange'
+    | 'waiting'
+    | 'load'
+    | 'error'
+    | 'animationStart'
+    | 'animationEnd'
+    | 'animationIteration'
+    | 'transitionEnd'
+    | 'doubleClick'
+    | 'pointerOver'
+    | 'pointerEnter'
+    | 'pointerDown'
+    | 'pointerMove'
+    | 'pointerUp'
+    | 'pointerCancel'
+    | 'pointerOut'
+    | 'pointerLeave'
+    | 'gotPointerCapture'
+    | 'lostPointerCapture';
+
+export type FireFunction = (element: Document | Element | Window | Node, event: Event) => boolean;
+export type FireObject = {
+    [K in EventType]: (element: Document | Element | Window | Node, options?: {}) => boolean;
+};
+export type CreateObject = {
+    [K in EventType]: (element: Document | Element | Window | Node, options?: {}) => Event;
+};
+
+export const createEvent: CreateObject;
+export const fireEvent: FireFunction & FireObject;
diff --git a/types/get-node-text.d.ts b/types/get-node-text.d.ts
new file mode 100644
index 00000000..5c5654b5
--- /dev/null
+++ b/types/get-node-text.d.ts
@@ -0,0 +1 @@
+export function getNodeText(node: HTMLElement): string;
diff --git a/types/get-queries-for-element.d.ts b/types/get-queries-for-element.d.ts
new file mode 100644
index 00000000..90e5626d
--- /dev/null
+++ b/types/get-queries-for-element.d.ts
@@ -0,0 +1,30 @@
+import { Matcher } from './matches';
+import * as queries from './queries';
+
+export type BoundFunction<T> = T extends (
+    attribute: string,
+    element: HTMLElement,
+    text: infer P,
+    options: infer Q,
+) => infer R
+    ? (text: P, options?: Q) => R
+    : T extends (a1: any, text: infer P, options: infer Q, waitForElementOptions: infer W) => infer R
+    ? (text: P, options?: Q, waitForElementOptions?: W) => R
+    : T extends (a1: any, text: infer P, options: infer Q) => infer R
+    ? (text: P, options?: Q) => R
+    : never;
+export type BoundFunctions<T> = { [P in keyof T]: BoundFunction<T[P]> };
+
+export type Query = (
+    container: HTMLElement,
+    ...args: any[]
+) => Error | Promise<HTMLElement[]> | Promise<HTMLElement> | HTMLElement[] | HTMLElement | null;
+
+export interface Queries {
+    [T: string]: Query;
+}
+
+export function getQueriesForElement<T extends Queries = typeof queries>(
+    element: HTMLElement,
+    queriesToBind?: T,
+): BoundFunctions<T>;
diff --git a/types/index.d.ts b/types/index.d.ts
new file mode 100644
index 00000000..406db91c
--- /dev/null
+++ b/types/index.d.ts
@@ -0,0 +1,24 @@
+// TypeScript Version: 3.8
+
+import { getQueriesForElement } from './get-queries-for-element';
+import * as queries from './queries';
+import * as queryHelpers from './query-helpers';
+
+declare const within: typeof getQueriesForElement;
+export { queries, queryHelpers, within };
+
+export * from './queries';
+export * from './query-helpers';
+export * from './screen';
+export * from './wait';
+export * from './wait-for';
+export * from './wait-for-dom-change';
+export * from './wait-for-element';
+export * from './wait-for-element-to-be-removed';
+export * from './matches';
+export * from './get-node-text';
+export * from './events';
+export * from './get-queries-for-element';
+export * from './pretty-dom';
+export * from './role-helpers';
+export * from './config';
diff --git a/types/matches.d.ts b/types/matches.d.ts
new file mode 100644
index 00000000..0b8dad4d
--- /dev/null
+++ b/types/matches.d.ts
@@ -0,0 +1,29 @@
+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 = (
+    textToMatch: string,
+    node: HTMLElement | null,
+    matcher: Matcher,
+    options?: MatcherOptions,
+) => boolean;
+
+export interface DefaultNormalizerOptions {
+    trim?: boolean;
+    collapseWhitespace?: boolean;
+}
+
+export function getDefaultNormalizer(options?: DefaultNormalizerOptions): NormalizerFn;
+
+// N.B. Don't expose fuzzyMatches + matches here: they're not public API
diff --git a/types/pretty-dom.d.ts b/types/pretty-dom.d.ts
new file mode 100644
index 00000000..bca6afb4
--- /dev/null
+++ b/types/pretty-dom.d.ts
@@ -0,0 +1,4 @@
+import { OptionsReceived } from 'pretty-format';
+
+export function prettyDOM(dom?: Element | HTMLDocument, maxLength?: number, options?: OptionsReceived): string | false;
+export function logDOM(dom?: Element | HTMLDocument, maxLength?: number, options?: OptionsReceived): void;
diff --git a/types/queries.d.ts b/types/queries.d.ts
new file mode 100644
index 00000000..6f8633ce
--- /dev/null
+++ b/types/queries.d.ts
@@ -0,0 +1,134 @@
+import { Matcher, MatcherOptions } from './matches';
+import { SelectorMatcherOptions } from './query-helpers';
+import { waitForOptions } from 'wait-for';
+
+export type QueryByBoundAttribute = (
+    container: HTMLElement,
+    id: Matcher,
+    options?: MatcherOptions,
+) => HTMLElement | null;
+
+export type AllByBoundAttribute = (container: HTMLElement, id: Matcher, options?: MatcherOptions) => HTMLElement[];
+
+export type FindAllByBoundAttribute = (
+    container: HTMLElement,
+    id: Matcher,
+    options?: MatcherOptions,
+    waitForElementOptions?: waitForOptions,
+) => Promise<HTMLElement[]>;
+
+export type GetByBoundAttribute = (container: HTMLElement, id: Matcher, options?: MatcherOptions) => HTMLElement;
+
+export type FindByBoundAttribute = (
+    container: HTMLElement,
+    id: Matcher,
+    options?: MatcherOptions,
+    waitForElementOptions?: waitForOptions,
+) => Promise<HTMLElement>;
+
+export type QueryByText = (container: HTMLElement, id: Matcher, options?: SelectorMatcherOptions) => HTMLElement | null;
+
+export type AllByText = (container: HTMLElement, id: Matcher, options?: SelectorMatcherOptions) => HTMLElement[];
+
+export type FindAllByText = (
+    container: HTMLElement,
+    id: Matcher,
+    options?: SelectorMatcherOptions,
+    waitForElementOptions?: waitForOptions,
+) => Promise<HTMLElement[]>;
+
+export type GetByText = (container: HTMLElement, id: Matcher, options?: SelectorMatcherOptions) => HTMLElement;
+
+export type FindByText = (
+    container: HTMLElement,
+    id: Matcher,
+    options?: SelectorMatcherOptions,
+    waitForElementOptions?: waitForOptions,
+) => Promise<HTMLElement>;
+
+export interface ByRoleOptions extends MatcherOptions {
+    /**
+     * If true includes elements in the query set that are usually excluded from
+     * the accessibility tree. `role="none"` or `role="presentation"` are included
+     * in either case.
+     */
+    hidden?: boolean;
+    /**
+     * Includes every role used in the `role` attribute
+     * For example *ByRole('progressbar', {queryFallbacks: true})` will find <div role="meter progresbar">`.
+     */
+    queryFallbacks?: boolean;
+    /**
+     * Only considers  elements with the specified accessible name.
+     */
+    name?: string | RegExp | ((accessibleName: string, element: Element) => boolean);
+}
+
+export type AllByRole = (container: HTMLElement, role: Matcher, options?: ByRoleOptions) => HTMLElement[];
+
+export type GetByRole = (container: HTMLElement, role: Matcher, options?: ByRoleOptions) => HTMLElement;
+
+export type QueryByRole = (container: HTMLElement, role: Matcher, options?: ByRoleOptions) => HTMLElement | null;
+
+export type FindByRole = (
+    container: HTMLElement,
+    role: Matcher,
+    options?: ByRoleOptions,
+    waitForElementOptions?: waitForOptions,
+) => Promise<HTMLElement>;
+
+export type FindAllByRole = (
+    container: HTMLElement,
+    role: Matcher,
+    options?: ByRoleOptions,
+    waitForElementOptions?: waitForOptions,
+) => Promise<HTMLElement[]>;
+
+export const getByLabelText: GetByText;
+export const getAllByLabelText: AllByText;
+export const queryByLabelText: QueryByText;
+export const queryAllByLabelText: AllByText;
+export const findByLabelText: FindByText;
+export const findAllByLabelText: FindAllByText;
+export const getByPlaceholderText: GetByBoundAttribute;
+export const getAllByPlaceholderText: AllByBoundAttribute;
+export const queryByPlaceholderText: QueryByBoundAttribute;
+export const queryAllByPlaceholderText: AllByBoundAttribute;
+export const findByPlaceholderText: FindByBoundAttribute;
+export const findAllByPlaceholderText: FindAllByBoundAttribute;
+export const getByText: GetByText;
+export const getAllByText: AllByText;
+export const queryByText: QueryByText;
+export const queryAllByText: AllByText;
+export const findByText: FindByText;
+export const findAllByText: FindAllByText;
+export const getByAltText: GetByBoundAttribute;
+export const getAllByAltText: AllByBoundAttribute;
+export const queryByAltText: QueryByBoundAttribute;
+export const queryAllByAltText: AllByBoundAttribute;
+export const findByAltText: FindByBoundAttribute;
+export const findAllByAltText: FindAllByBoundAttribute;
+export const getByTitle: GetByBoundAttribute;
+export const getAllByTitle: AllByBoundAttribute;
+export const queryByTitle: QueryByBoundAttribute;
+export const queryAllByTitle: AllByBoundAttribute;
+export const findByTitle: FindByBoundAttribute;
+export const findAllByTitle: FindAllByBoundAttribute;
+export const getByDisplayValue: GetByBoundAttribute;
+export const getAllByDisplayValue: AllByBoundAttribute;
+export const queryByDisplayValue: QueryByBoundAttribute;
+export const queryAllByDisplayValue: AllByBoundAttribute;
+export const findByDisplayValue: FindByBoundAttribute;
+export const findAllByDisplayValue: FindAllByBoundAttribute;
+export const getByRole: GetByRole;
+export const getAllByRole: AllByRole;
+export const queryByRole: QueryByRole;
+export const queryAllByRole: AllByRole;
+export const findByRole: FindByRole;
+export const findAllByRole: FindAllByRole;
+export const getByTestId: GetByBoundAttribute;
+export const getAllByTestId: AllByBoundAttribute;
+export const queryByTestId: QueryByBoundAttribute;
+export const queryAllByTestId: AllByBoundAttribute;
+export const findByTestId: FindByBoundAttribute;
+export const findAllByTestId: FindAllByBoundAttribute;
diff --git a/types/query-helpers.d.ts b/types/query-helpers.d.ts
new file mode 100644
index 00000000..63a1f69b
--- /dev/null
+++ b/types/query-helpers.d.ts
@@ -0,0 +1,46 @@
+import { Matcher, MatcherOptions } from './matches';
+
+export interface SelectorMatcherOptions extends MatcherOptions {
+    selector?: string;
+}
+
+export type QueryByAttribute = (
+    attribute: string,
+    container: HTMLElement,
+    id: Matcher,
+    options?: MatcherOptions,
+) => HTMLElement | null;
+
+export type AllByAttribute = (
+    attribute: string,
+    container: HTMLElement,
+    id: Matcher,
+    options?: MatcherOptions,
+) => HTMLElement[];
+
+export const queryByAttribute: QueryByAttribute;
+export const queryAllByAttribute: AllByAttribute;
+export function getElementError(message: string, container: HTMLElement): Error;
+
+/**
+ * query methods have a common call signature. Only the return type differs.
+ */
+export type QueryMethod<Arguments extends any[], Return> = (container: HTMLElement, ...args: Arguments) => Return;
+export type QueryBy<Arguments extends any[]> = QueryMethod<Arguments, HTMLElement | null>;
+export type GetAllBy<Arguments extends any[]> = QueryMethod<Arguments, HTMLElement[]>;
+export type FindAllBy<Arguments extends any[]> = QueryMethod<Arguments, Promise<HTMLElement[]>>;
+export type GetBy<Arguments extends any[]> = QueryMethod<Arguments, HTMLElement>;
+export type FindBy<Arguments extends any[]> = QueryMethod<Arguments, Promise<HTMLElement>>;
+
+export type BuiltQueryMethods<Arguments extends any[]> = [
+    QueryBy<Arguments>,
+    GetAllBy<Arguments>,
+    GetBy<Arguments>,
+    FindAllBy<Arguments>,
+    FindBy<Arguments>
+];
+export function buildQueries<Arguments extends any[]>(
+    queryByAll: GetAllBy<Arguments>,
+    getMultipleError: (container: HTMLElement, ...args: Arguments) => string,
+    getMissingError: (container: HTMLElement, ...args: Arguments) => string,
+): BuiltQueryMethods<Arguments>;
diff --git a/types/role-helpers.d.ts b/types/role-helpers.d.ts
new file mode 100644
index 00000000..3dd35b78
--- /dev/null
+++ b/types/role-helpers.d.ts
@@ -0,0 +1,6 @@
+export function logRoles(container: HTMLElement): string;
+export function getRoles(container: HTMLElement): { [index: string]: HTMLElement[] };
+/**
+ * https://testing-library.com/docs/dom-testing-library/api-helpers#isinaccessible
+ */
+export function isInaccessible(element: Element): boolean;
diff --git a/types/screen.d.ts b/types/screen.d.ts
new file mode 100644
index 00000000..906b59ef
--- /dev/null
+++ b/types/screen.d.ts
@@ -0,0 +1,17 @@
+import { BoundFunctions, Queries } from './get-queries-for-element';
+import * as queries from './queries';
+import { OptionsReceived } from 'pretty-format';
+
+export type Screen<Q extends Queries = typeof queries> = BoundFunctions<Q> & {
+    /**
+     * Convenience function for `pretty-dom` which also allows an array
+     * of elements
+     */
+    debug: (
+        element: Element | HTMLDocument | Array<Element | HTMLDocument>,
+        maxLength?: number,
+        options?: OptionsReceived,
+    ) => void;
+};
+
+export const screen: Screen;
diff --git a/types/tsconfig.json b/types/tsconfig.json
new file mode 100644
index 00000000..c4da27db
--- /dev/null
+++ b/types/tsconfig.json
@@ -0,0 +1,17 @@
+// this additional tsconfig is required by dtslint
+// see: https://github.com/Microsoft/dtslint#typestsconfigjson
+{
+  "compilerOptions": {
+    "module": "commonjs",
+    "lib": ["es6", "dom"],
+    "noImplicitAny": true,
+    "noImplicitThis": true,
+    "strictNullChecks": true,
+    "strictFunctionTypes": true,
+    "noEmit": true,
+
+    // If the library is an external module (uses `export`), this allows your test file to import "mylib" instead of "./index".
+    // If the library is global (cannot be imported via `import` or `require`), leave this out.
+    "baseUrl": "."
+  }
+}
diff --git a/types/tslint.json b/types/tslint.json
new file mode 100644
index 00000000..5d45232f
--- /dev/null
+++ b/types/tslint.json
@@ -0,0 +1,8 @@
+{
+  "extends": ["dtslint/dtslint.json"],
+  "rules": {
+    "no-useless-files": false,
+    "no-relative-import-in-test": false,
+    "semicolon": false
+  }
+}
diff --git a/types/wait-for-dom-change.d.ts b/types/wait-for-dom-change.d.ts
new file mode 100644
index 00000000..2fe72c10
--- /dev/null
+++ b/types/wait-for-dom-change.d.ts
@@ -0,0 +1,3 @@
+import { waitForOptions } from "index";
+
+export function waitForDomChange(options?: waitForOptions): Promise<any>;
diff --git a/types/wait-for-element-to-be-removed.d.ts b/types/wait-for-element-to-be-removed.d.ts
new file mode 100644
index 00000000..42a891ac
--- /dev/null
+++ b/types/wait-for-element-to-be-removed.d.ts
@@ -0,0 +1,6 @@
+import { waitForOptions } from "wait-for";
+
+export function waitForElementToBeRemoved<T>(
+    callback: (() => T) | T,
+    options?: waitForOptions,
+): Promise<T>;
diff --git a/types/wait-for-element.d.ts b/types/wait-for-element.d.ts
new file mode 100644
index 00000000..f21fb112
--- /dev/null
+++ b/types/wait-for-element.d.ts
@@ -0,0 +1,3 @@
+import { waitForOptions } from "wait-for";
+
+export function waitForElement<T>(callback: () => T, options?: waitForOptions): Promise<T>;
diff --git a/types/wait-for.d.ts b/types/wait-for.d.ts
new file mode 100644
index 00000000..3c39073b
--- /dev/null
+++ b/types/wait-for.d.ts
@@ -0,0 +1,11 @@
+export interface waitForOptions {
+    container?: HTMLElement;
+    timeout?: number;
+    interval?: number;
+    mutationObserverOptions?: MutationObserverInit;
+}
+
+export function waitFor<T>(
+    callback: () => T,
+    options?: waitForOptions,
+): Promise<T>;
diff --git a/types/wait.d.ts b/types/wait.d.ts
new file mode 100644
index 00000000..3763e7bd
--- /dev/null
+++ b/types/wait.d.ts
@@ -0,0 +1,7 @@
+export function wait(
+    callback?: () => void,
+    options?: {
+        timeout?: number;
+        interval?: number;
+    },
+): Promise<void>;