Skip to content

Commit

Permalink
Match.tag + Match.withReturnType can use literals without as const (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-smart authored Feb 28, 2025
1 parent 0c0d163 commit 367bb35
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 45 deletions.
5 changes: 5 additions & 0 deletions .changeset/gentle-moons-act.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": patch
---

Match.tag + Match.withReturnType can use literals without as const
36 changes: 23 additions & 13 deletions packages/effect/src/Match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,15 +511,15 @@ export const whenAnd: <
*/
export const discriminator: <D extends string>(
field: D
) => <R, P extends Types.Tags<D, R> & string, Ret, B extends Ret>(
...pattern: [first: P, ...values: Array<P>, f: (_: Extract<R, Record<D, P>>) => B]
) => <R, P extends Types.Tags<D, R> & string, Ret, Fn extends (_: Extract<R, Record<D, P>>) => Ret>(
...pattern: [first: P, ...values: Array<P>, f: Fn]
) => <I, F, A, Pr>(
self: Matcher<I, F, R, A, Pr, Ret>
) => Matcher<
I,
Types.AddWithout<F, Extract<R, Record<D, P>>>,
Types.ApplyFilters<I, Types.AddWithout<F, Extract<R, Record<D, P>>>>,
B | A,
A | ReturnType<Fn>,
Pr,
Ret
> = internal.discriminator
Expand Down Expand Up @@ -560,19 +560,19 @@ export const discriminator: <D extends string>(
*/
export const discriminatorStartsWith: <D extends string>(
field: D
) => <R, P extends string, Ret, B extends Ret>(
) => <R, P extends string, Ret, Fn extends (_: Extract<R, Record<D, `${P}${string}`>>) => Ret>(
pattern: P,
f: (_: Extract<R, Record<D, `${P}${string}`>>) => B
f: Fn
) => <I, F, A, Pr>(
self: Matcher<I, F, R, A, Pr, Ret>
) => Matcher<
I,
Types.AddWithout<F, Extract<R, Record<D, `${P}${string}`>>>,
Types.ApplyFilters<I, Types.AddWithout<F, Extract<R, Record<D, `${P}${string}`>>>>,
B | A,
A | ReturnType<Fn>,
Pr,
Ret
> = internal.discriminatorStartsWith as any
> = internal.discriminatorStartsWith

/**
* Matches values based on a field that serves as a discriminator, mapping each
Expand Down Expand Up @@ -716,15 +716,20 @@ export const discriminatorsExhaustive: <D extends string>(
* @category Defining patterns
* @since 1.0.0
*/
export const tag: <R, P extends Types.Tags<"_tag", R> & string, Ret, B extends Ret>(
...pattern: [first: P, ...values: Array<P>, f: (_: Extract<T.NoInfer<R>, Record<"_tag", P>>) => B]
export const tag: <
R,
P extends Types.Tags<"_tag", R> & string,
Ret,
Fn extends (_: Extract<R, Record<"_tag", P>>) => Ret
>(
...pattern: [first: P, ...values: Array<P>, f: Fn]
) => <I, F, A, Pr>(
self: Matcher<I, F, R, A, Pr, Ret>
) => Matcher<
I,
Types.AddWithout<F, Extract<R, Record<"_tag", P>>>,
Types.ApplyFilters<I, Types.AddWithout<F, Extract<R, Record<"_tag", P>>>>,
B | A,
ReturnType<Fn> | A,
Pr,
Ret
> = internal.tag
Expand Down Expand Up @@ -758,16 +763,21 @@ export const tag: <R, P extends Types.Tags<"_tag", R> & string, Ret, B extends R
* @category Defining patterns
* @since 1.0.0
*/
export const tagStartsWith: <R, P extends string, Ret, B extends Ret>(
export const tagStartsWith: <
R,
P extends string,
Ret,
Fn extends (_: Extract<R, Record<"_tag", `${P}${string}`>>) => Ret
>(
pattern: P,
f: (_: Extract<T.NoInfer<R>, Record<"_tag", `${P}${string}`>>) => B
f: Fn
) => <I, F, A, Pr>(
self: Matcher<I, F, R, A, Pr, Ret>
) => Matcher<
I,
Types.AddWithout<F, Extract<R, Record<"_tag", `${P}${string}`>>>,
Types.ApplyFilters<I, Types.AddWithout<F, Extract<R, Record<"_tag", `${P}${string}`>>>>,
B | A,
ReturnType<Fn> | A,
Pr,
Ret
> = internal.tagStartsWith
Expand Down
78 changes: 46 additions & 32 deletions packages/effect/src/internal/matcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,38 +321,47 @@ export const whenAnd = <
}

/** @internal */
export const discriminator =
<D extends string>(field: D) =>
<R, P extends Types.Tags<D, R> & string, Ret, B extends Ret>(
...pattern: [
first: P,
...values: Array<P>,
f: (_: Extract<R, Record<D, P>>) => B
]
) => {
const f = pattern[pattern.length - 1]
const values: Array<P> = pattern.slice(0, -1) as any
const pred = values.length === 1
? (_: any) => _[field] === values[0]
: (_: any) => values.includes(_[field])

return <I, F, A, Pr>(
self: Matcher<I, F, R, A, Pr, Ret>
): Matcher<
I,
Types.AddWithout<F, Extract<R, Record<D, P>>>,
Types.ApplyFilters<I, Types.AddWithout<F, Extract<R, Record<D, P>>>>,
A | B,
Pr,
Ret
> => (self as any).add(makeWhen(pred, f as any)) as any
}
export const discriminator = <D extends string>(field: D) =>
<
R,
P extends Types.Tags<D, R> & string,
Ret,
Fn extends (_: Extract<R, Record<D, P>>) => Ret
>(
...pattern: [
first: P,
...values: Array<P>,
f: Fn
]
) => {
const f = pattern[pattern.length - 1]
const values: Array<P> = pattern.slice(0, -1) as any
const pred = values.length === 1
? (_: any) => _[field] === values[0]
: (_: any) => values.includes(_[field])

return <I, F, A, Pr>(
self: Matcher<I, F, R, A, Pr, Ret>
): Matcher<
I,
Types.AddWithout<F, Extract<R, Record<D, P>>>,
Types.ApplyFilters<I, Types.AddWithout<F, Extract<R, Record<D, P>>>>,
A | ReturnType<Fn>,
Pr,
Ret
> => (self as any).add(makeWhen(pred, f as any)) as any
}

/** @internal */
export const discriminatorStartsWith = <D extends string>(field: D) =>
<R, P extends string, Ret, B extends Ret>(
<
R,
P extends string,
Ret,
Fn extends (_: Extract<R, Record<D, `${P}${string}`>>) => Ret
>(
pattern: P,
f: (_: Extract<R, Record<D, `${P}${string}`>>) => B
f: Fn
) => {
const pred = (_: any) => typeof _[field] === "string" && _[field].startsWith(pattern)

Expand All @@ -365,7 +374,7 @@ export const discriminatorStartsWith = <D extends string>(field: D) =>
I,
Types.AddWithout<F, Extract<R, Record<D, `${P}${string}`>>>
>,
A | B,
A | ReturnType<Fn>,
Pr,
Ret
> => (self as any).add(makeWhen(pred, f as any)) as any
Expand Down Expand Up @@ -427,19 +436,24 @@ export const discriminatorsExhaustive: <D extends string>(
}

/** @internal */
export const tag: <R, P extends Types.Tags<"_tag", R> & string, Ret, B extends Ret>(
export const tag: <
R,
P extends Types.Tags<"_tag", R> & string,
Ret,
Fn extends (_: Extract<R, Record<"_tag", P>>) => Ret
>(
...pattern: [
first: P,
...values: Array<P>,
f: (_: Extract<R, Record<"_tag", P>>) => B
f: Fn
]
) => <I, F, A, Pr>(
self: Matcher<I, F, R, A, Pr, Ret>
) => Matcher<
I,
Types.AddWithout<F, Extract<R, Record<"_tag", P>>>,
Types.ApplyFilters<I, Types.AddWithout<F, Extract<R, Record<"_tag", P>>>>,
B | A,
ReturnType<Fn> | A,
Pr,
Ret
> = discriminator("_tag")
Expand Down
11 changes: 11 additions & 0 deletions packages/effect/test/Match.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -845,4 +845,15 @@ describe("Match", () => {
)
strictEqual(result, "default")
})

it("tag + withReturnType doesn't need as const for string literals", () => {
type Value = { _tag: "A"; a: number } | { _tag: "B"; b: number }
const result = M.value<Value>({ _tag: "A", a: 1 }).pipe(
M.withReturnType<"a" | "b">(),
M.tag("A", () => "a"),
M.tag("B", () => "b"),
M.exhaustive
)
strictEqual(result, "a")
})
})

0 comments on commit 367bb35

Please # to comment.