diff --git a/README.md b/README.md
index e6b2b847..94f52d3f 100644
--- a/README.md
+++ b/README.md
@@ -1545,6 +1545,23 @@ console.log(isMatching(P.object.empty(), null)); // false
console.log(isMatching(P.object.empty(), undefined)); // false
```
+### `P.object.exact({...})`
+
+`P.object.exact({...})` matches objects that contain exactly the set of defined in the pattern.
+
+```ts
+import { match, P } from 'ts-pattern';
+
+const fn = (input: unknown) =>
+ match(input)
+ .with(P.object.exact({ a: P.any }), () => 'Objects with a single `a` key that contains anything.')
+ .otherwise(() => '❌');
+
+console.log(fn({})); // ❌
+console.log(fn({ a: 1 })); // ✅
+console.log(fn({ a: 1, b: 2 })); // ❌
+```
+
## Types
### `P.infer`
diff --git a/src/types/Pattern.ts b/src/types/Pattern.ts
index 6ca297c1..d6e295bd 100644
--- a/src/types/Pattern.ts
+++ b/src/types/Pattern.ts
@@ -695,7 +695,7 @@ export type ObjectChainable<
* () => 'Objects with a single `a` key that contains anything.'
* )
*/
- >(
+ exact>(
pattern: pattern
): Chainable, never>>;
},
diff --git a/tests/object.test.ts b/tests/object.test.ts
index 3c73efa7..38bdfbed 100644
--- a/tests/object.test.ts
+++ b/tests/object.test.ts
@@ -106,4 +106,22 @@ describe('Object', () => {
expect(fn(null)).toEqual('no');
});
});
+
+ describe('P.object.exact({...})', () => {
+ it('should only catch the literal `{}`.', () => {
+ const fn = (input: object) =>
+ match(input)
+ .with(P.object.exact({ a: P.any }), (obj) => {
+ type t = Expect>;
+ return 'yes';
+ })
+ // @ts-expect-error: non empty object aren't caught
+ .exhaustive();
+ expect(fn({})).toEqual('yes');
+ expect(() => fn({ hello: 'world' })).toThrow();
+ expect(() => fn(() => {})).toThrow();
+ expect(() => fn([1, 2, 3])).toThrow();
+ expect(() => fn([])).toThrow();
+ });
+ });
});