Skip to content

Commit cacf19a

Browse files
authored
Merge pull request #252 from gvergnaud/gvergnaud/fix-exhaustive-with-p-non-nullable
fix: exhaustive checking with nested P.nonNullable patterns
2 parents 43035d1 + 5afa02f commit cacf19a

File tree

5 files changed

+34
-5
lines changed

5 files changed

+34
-5
lines changed

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ts-pattern",
3-
"version": "5.1.1",
3+
"version": "5.1.2",
44
"description": " The exhaustive Pattern Matching library for TypeScript.",
55
"type": "module",
66
"source": "src/index.ts",

src/types/IsMatching.ts

+5
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ export type IsMatching<a, b> = true extends IsUnion<a> | IsUnion<b>
6565
// match everything.
6666
unknown extends b
6767
? true
68+
: // Special case for `{}`, because this is the type
69+
// of the inverted `P.nonNullable` wildcard pattern,
70+
// which should match all objects.
71+
{} extends b
72+
? true
6873
: b extends Primitives
6974
? // if the pattern is a primitive, we want to check if there is
7075
// an overlap between a and b!

tests/type-is-matching.test.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,9 @@ describe('IsMatching', () => {
201201
false
202202
>
203203
>,
204-
Expect<Equal<IsMatching<{ type: 'a' }, {}>, false>>,
204+
// the empty object matches everything except null | undefined
205+
// just like the `{}` type.
206+
Expect<Equal<IsMatching<{ type: 'a' }, {}>, true>>,
205207
Expect<Equal<IsMatching<{}, { type: 'a' }>, false>>
206208
];
207209
});

tests/wildcards.test.ts

+23-1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,28 @@ describe('wildcards', () => {
113113
'matched'
114114
);
115115
});
116+
117+
it('combined with exhaustive, it should consider all values except null and undefined to be handled', () => {
118+
const fn1 = (input: string | number | null | undefined) =>
119+
match(input)
120+
.with(P.nonNullable, (x) => {
121+
type t = Expect<Equal<typeof x, string | number>>;
122+
})
123+
.with(P.nullish, () => {})
124+
// should type-check
125+
.exhaustive();
126+
127+
const fn2 = (input: { nested: string | number | null | undefined }) =>
128+
match(input)
129+
.with({ nested: P.nonNullable }, (x) => {
130+
type t = Expect<Equal<typeof x, { nested: string | number }>>;
131+
})
132+
.with({ nested: P.nullish }, (x) => {
133+
type t = Expect<Equal<typeof x, { nested: null | undefined }>>;
134+
})
135+
// should type-check
136+
.exhaustive();
137+
});
116138
});
117139

118140
it('should match String, Number and Boolean wildcards', () => {
@@ -202,7 +224,7 @@ describe('wildcards', () => {
202224
expect(
203225
match(value)
204226
.with(P._, () => 'yes')
205-
.run()
227+
.exhaustive()
206228
).toEqual('yes');
207229
});
208230
});

0 commit comments

Comments
 (0)