Skip to content

Commit e71889f

Browse files
fix(Match): handle null/undefined in Match.tag and Match.tagStartsWith (#6018)
1 parent 0023c19 commit e71889f

File tree

3 files changed

+42
-3
lines changed

3 files changed

+42
-3
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"effect": patch
3+
---
4+
5+
fix(Match): handle null/undefined in `Match.tag` and `Match.tagStartsWith`
6+
7+
Added null checks to `discriminator` and `discriminatorStartsWith` predicates to prevent crashes when matching nullable union types.
8+
9+
Fixes #6017

packages/effect/src/internal/matcher.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,8 @@ export const discriminator = <D extends string>(field: D) =>
351351
const f = pattern[pattern.length - 1]
352352
const values: Array<P> = pattern.slice(0, -1) as any
353353
const pred = values.length === 1
354-
? (_: any) => _[field] === values[0]
355-
: (_: any) => values.includes(_[field])
354+
? (_: any) => _ != null && _[field] === values[0]
355+
: (_: any) => _ != null && values.includes(_[field])
356356

357357
return <I, F, A, Pr>(
358358
self: Matcher<I, F, R, A, Pr, Ret>
@@ -377,7 +377,7 @@ export const discriminatorStartsWith = <D extends string>(field: D) =>
377377
pattern: P,
378378
f: Fn
379379
) => {
380-
const pred = (_: any) => typeof _[field] === "string" && _[field].startsWith(pattern)
380+
const pred = (_: any) => _ != null && typeof _[field] === "string" && _[field].startsWith(pattern)
381381

382382
return <I, F, A, Pr>(
383383
self: Matcher<I, F, R, A, Pr, Ret>

packages/effect/test/Match.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,36 @@ describe("Match", () => {
207207
doesNotThrow(() => match(undefined))
208208
})
209209

210+
it("Match.tag with nullable union", () => {
211+
const match = M.type<{ _tag: "A" } | undefined>().pipe(
212+
M.tag("A", () => "matched A"),
213+
M.when(undefined, () => "matched undefined"),
214+
M.exhaustive
215+
)
216+
strictEqual(match({ _tag: "A" }), "matched A")
217+
strictEqual(match(undefined), "matched undefined")
218+
})
219+
220+
it("Match.tag with null union", () => {
221+
const match = M.type<{ _tag: "A" } | null>().pipe(
222+
M.tag("A", () => "matched A"),
223+
M.when(null, () => "matched null"),
224+
M.exhaustive
225+
)
226+
strictEqual(match({ _tag: "A" }), "matched A")
227+
strictEqual(match(null), "matched null")
228+
})
229+
230+
it("Match.tagStartsWith with nullable union", () => {
231+
const match = M.type<{ _tag: "A.B" } | undefined>().pipe(
232+
M.tagStartsWith("A", () => "matched A prefix"),
233+
M.when(undefined, () => "matched undefined"),
234+
M.exhaustive
235+
)
236+
strictEqual(match({ _tag: "A.B" }), "matched A prefix")
237+
strictEqual(match(undefined), "matched undefined")
238+
})
239+
210240
it("discriminator multiple", () => {
211241
const result = pipe(
212242
M.value(Either.right(0)),

0 commit comments

Comments
 (0)