Skip to content

Commit 17f40d6

Browse files
tim-smarteffect-bot
authored andcommitted
improve Effect.filter* types to exclude candidates in fallback functions (#4814)
1 parent 2fc4deb commit 17f40d6

File tree

5 files changed

+132
-156
lines changed

5 files changed

+132
-156
lines changed

.changeset/hot-bats-sparkle.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"effect": minor
3+
---
4+
5+
improve Effect.filter\* types to exclude candidates in fallback functions

packages/effect/dtslint/Effect.tst.ts

+30-7
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,17 @@ describe("Effect", () => {
436436
)
437437
)
438438
).type.toBe<Effect.Effect<"a", "a">>()
439+
expect(
440+
Effect.succeed<"a" | "b">("a").pipe(
441+
Effect.filterOrFail(
442+
(s): s is "a" => s === "a",
443+
(x) => {
444+
expect(x).type.toBe<"b">()
445+
return "a" as const
446+
}
447+
)
448+
)
449+
).type.toBe<Effect.Effect<"a", "a">>()
439450
})
440451

441452
it("filterOrDie", () => {
@@ -450,6 +461,18 @@ describe("Effect", () => {
450461
)
451462
)
452463
).type.toBe<Effect.Effect<"a">>()
464+
465+
expect(
466+
Effect.succeed<"a" | "b">("a").pipe(
467+
Effect.filterOrDie(
468+
(s): s is "a" => s === "a",
469+
(x) => {
470+
expect(x).type.toBe<"b">()
471+
return "fail"
472+
}
473+
)
474+
)
475+
).type.toBe<Effect.Effect<"a">>()
453476
})
454477

455478
it("filterOrDieMessage", () => {
@@ -1256,17 +1279,17 @@ describe("Effect", () => {
12561279
expect(pipe(
12571280
primitiveNumberOrString,
12581281
Effect.liftPredicate(Predicate.isString, (sn) => {
1259-
expect(sn).type.toBe<unknown>()
1282+
expect(sn).type.toBe<number>()
12601283
return "b" as const
12611284
})
12621285
)).type.toBe<Effect.Effect<string, "b">>()
12631286
expect(Effect.liftPredicate(primitiveNumberOrString, Predicate.isString, (sn) => {
1264-
expect(sn).type.toBe<string | number>()
1287+
expect(sn).type.toBe<number>()
12651288
return "b" as const
12661289
})).type.toBe<Effect.Effect<string, "b">>()
12671290

12681291
expect(Effect.liftPredicate(hole<Predicate.Refinement<string | number, number>>(), (sn) => {
1269-
expect(sn).type.toBe<string | number>()
1292+
expect(sn).type.toBe<string>()
12701293
return "b" as const
12711294
})).type.toBe<(a: string | number) => Effect.Effect<number, "b">>()
12721295
expect(Effect.liftPredicate(Predicate.isString, (sn) => {
@@ -1282,7 +1305,7 @@ describe("Effect", () => {
12821305
return typeof sn === "number"
12831306
},
12841307
(sn) => {
1285-
expect(sn).type.toBe<string | number>()
1308+
expect(sn).type.toBe<string>()
12861309
return "b" as const
12871310
}
12881311
)
@@ -1291,7 +1314,7 @@ describe("Effect", () => {
12911314
expect(sn).type.toBe<string | number>()
12921315
return typeof sn === "number"
12931316
}, (sn) => {
1294-
expect(sn).type.toBe<string | number>()
1317+
expect(sn).type.toBe<string>()
12951318
return "b" as const
12961319
})).type.toBe<Effect.Effect<number, "b">>()
12971320

@@ -1310,12 +1333,12 @@ describe("Effect", () => {
13101333
expect(pipe(
13111334
primitiveNumber,
13121335
Effect.liftPredicate(predicateNumbersOrStrings, (n) => {
1313-
expect(n).type.toBe<string | number>()
1336+
expect(n).type.toBe<number>()
13141337
return "b" as const
13151338
})
13161339
)).type.toBe<Effect.Effect<number, "b">>()
13171340
expect(Effect.liftPredicate(primitiveNumber, predicateNumbersOrStrings, (n) => {
1318-
expect(n).type.toBe<string | number>()
1341+
expect(n).type.toBe<number>()
13191342
return "b" as const
13201343
})).type.toBe<Effect.Effect<number, "b">>()
13211344

packages/effect/src/Effect.ts

+48-57
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,15 @@ import * as Scheduler from "./Scheduler.js"
6060
import type * as Scope from "./Scope.js"
6161
import type * as Supervisor from "./Supervisor.js"
6262
import type * as Tracer from "./Tracer.js"
63-
import type { Concurrency, Contravariant, Covariant, NoExcessProperties, NoInfer, NotFunction } from "./Types.js"
63+
import type {
64+
Concurrency,
65+
Contravariant,
66+
Covariant,
67+
EqualsWith,
68+
NoExcessProperties,
69+
NoInfer,
70+
NotFunction
71+
} from "./Types.js"
6472
import type * as Unify from "./Unify.js"
6573
import { isGeneratorFunction, type YieldWrap } from "./Utils.js"
6674

@@ -4968,13 +4976,15 @@ export const uninterruptibleMask: <A, E, R>(
49684976
* @since 3.4.0
49694977
*/
49704978
export const liftPredicate: {
4971-
<A, B extends A, E>(
4972-
refinement: Refinement<A, B>,
4973-
orFailWith: (a: A) => E
4974-
): (a: A) => Effect<B, E>
4975-
<B extends A, E, A = B>(predicate: Predicate<A>, orFailWith: (a: A) => E): (a: B) => Effect<B, E>
4976-
<A, E, B extends A>(self: A, refinement: Refinement<A, B>, orFailWith: (a: A) => E): Effect<B, E>
4977-
<B extends A, E, A = B>(self: B, predicate: Predicate<A>, orFailWith: (a: A) => E): Effect<B, E>
4979+
<T extends A, E, B extends T = T, A = T>(
4980+
predicate: Refinement<T, B> | Predicate<T>,
4981+
orFailWith: (a: EqualsWith<T, B, A, Exclude<A, B>>) => E
4982+
): (a: A) => Effect<EqualsWith<T, B, A, B>, E>
4983+
<A, E, B extends A = A>(
4984+
self: A,
4985+
predicate: Refinement<A, B> | Predicate<A>,
4986+
orFailWith: (a: EqualsWith<A, B, A, Exclude<A, B>>) => E
4987+
): Effect<B, E>
49784988
} = effect.liftPredicate
49794989

49804990
/**
@@ -8311,20 +8321,15 @@ export {
83118321
* @category Filtering
83128322
*/
83138323
export const filterOrDie: {
8314-
<A, B extends A>(
8315-
refinement: Refinement<NoInfer<A>, B>,
8316-
orDieWith: (a: NoInfer<A>) => unknown
8324+
<A, B extends A = A>(
8325+
predicate: Predicate<NoInfer<A>> | Refinement<NoInfer<A>, B>,
8326+
orDieWith: (a: EqualsWith<A, B, A, Exclude<A, B>>) => unknown
83178327
): <E, R>(self: Effect<A, E, R>) => Effect<B, E, R>
8318-
<A>(
8319-
predicate: Predicate<NoInfer<A>>,
8320-
orDieWith: (a: NoInfer<A>) => unknown
8321-
): <E, R>(self: Effect<A, E, R>) => Effect<A, E, R>
83228328
<A, E, R, B extends A>(
83238329
self: Effect<A, E, R>,
8324-
refinement: Refinement<A, B>,
8325-
orDieWith: (a: A) => unknown
8330+
predicate: Predicate<A> | Refinement<A, B>,
8331+
orDieWith: (a: EqualsWith<A, B, A, Exclude<A, B>>) => unknown
83268332
): Effect<B, E, R>
8327-
<A, E, R>(self: Effect<A, E, R>, predicate: Predicate<A>, orDieWith: (a: A) => unknown): Effect<A, E, R>
83288333
} = effect.filterOrDie
83298334

83308335
/**
@@ -8340,13 +8345,15 @@ export const filterOrDie: {
83408345
* @category Filtering
83418346
*/
83428347
export const filterOrDieMessage: {
8343-
<A, B extends A>(
8344-
refinement: Refinement<NoInfer<A>, B>,
8348+
<A, B extends A = A>(
8349+
predicate: Predicate<NoInfer<A>> | Refinement<NoInfer<A>, B>,
83458350
message: string
83468351
): <E, R>(self: Effect<A, E, R>) => Effect<B, E, R>
8347-
<A>(predicate: Predicate<NoInfer<A>>, message: string): <E, R>(self: Effect<A, E, R>) => Effect<A, E, R>
8348-
<A, E, R, B extends A>(self: Effect<A, E, R>, refinement: Refinement<A, B>, message: string): Effect<B, E, R>
8349-
<A, E, R>(self: Effect<A, E, R>, predicate: Predicate<A>, message: string): Effect<A, E, R>
8352+
<A, E, R, B extends A = A>(
8353+
self: Effect<A, E, R>,
8354+
predicate: Predicate<A> | Refinement<A, B>,
8355+
message: string
8356+
): Effect<B, E, R>
83508357
} = effect.filterOrDieMessage
83518358

83528359
/**
@@ -8363,24 +8370,15 @@ export const filterOrDieMessage: {
83638370
* @category Filtering
83648371
*/
83658372
export const filterOrElse: {
8366-
<A, B extends A, C, E2, R2>(
8367-
refinement: Refinement<NoInfer<A>, B>,
8368-
orElse: (a: NoInfer<A>) => Effect<C, E2, R2>
8373+
<A, C, E2, R2, B extends A = A>(
8374+
predicate: Predicate<NoInfer<A>> | Refinement<NoInfer<A>, B>,
8375+
orElse: (a: EqualsWith<A, B, NoInfer<A>, Exclude<NoInfer<A>, B>>) => Effect<C, E2, R2>
83698376
): <E, R>(self: Effect<A, E, R>) => Effect<B | C, E2 | E, R2 | R>
8370-
<A, B, E2, R2>(
8371-
predicate: Predicate<NoInfer<A>>,
8372-
orElse: (a: NoInfer<A>) => Effect<B, E2, R2>
8373-
): <E, R>(self: Effect<A, E, R>) => Effect<A | B, E2 | E, R2 | R>
8374-
<A, E, R, B extends A, C, E2, R2>(
8377+
<A, E, R, C, E2, R2, B extends A = A>(
83758378
self: Effect<A, E, R>,
8376-
refinement: Refinement<A, B>,
8377-
orElse: (a: A) => Effect<C, E2, R2>
8379+
predicate: Predicate<A> | Refinement<A, B>,
8380+
orElse: (a: EqualsWith<A, B, A, Exclude<A, B>>) => Effect<C, E2, R2>
83788381
): Effect<B | C, E | E2, R | R2>
8379-
<A, E, R, B, E2, R2>(
8380-
self: Effect<A, E, R>,
8381-
predicate: Predicate<A>,
8382-
orElse: (a: A) => Effect<B, E2, R2>
8383-
): Effect<A | B, E | E2, R | R2>
83848382
} = effect.filterOrElse
83858383

83868384
/**
@@ -8434,29 +8432,22 @@ export const filterOrElse: {
84348432
* @category Filtering
84358433
*/
84368434
export const filterOrFail: {
8437-
<A, B extends A, E2>(
8438-
refinement: Refinement<NoInfer<A>, B>,
8439-
orFailWith: (a: NoInfer<A>) => E2
8435+
<A, E2, B extends A = A>(
8436+
predicate: Predicate<NoInfer<A>> | Refinement<NoInfer<A>, B>,
8437+
orFailWith: (a: EqualsWith<A, B, NoInfer<A>, Exclude<NoInfer<A>, B>>) => E2
84408438
): <E, R>(self: Effect<A, E, R>) => Effect<B, E2 | E, R>
8441-
<A, E2>(
8442-
predicate: Predicate<NoInfer<A>>,
8443-
orFailWith: (a: NoInfer<A>) => E2
8444-
): <E, R>(self: Effect<A, E, R>) => Effect<A, E2 | E, R>
8445-
<A, B extends A>(
8446-
refinement: Refinement<NoInfer<A>, B>
8447-
): <E, R>(self: Effect<A, E, R>) => Effect<B, Cause.NoSuchElementException | E, R>
8448-
<A>(predicate: Predicate<NoInfer<A>>): <E, R>(self: Effect<A, E, R>) => Effect<A, Cause.NoSuchElementException | E, R>
8449-
<A, E, R, B extends A, E2>(
8439+
<A, E, R, E2, B extends A = A>(
84508440
self: Effect<A, E, R>,
8451-
refinement: Refinement<A, B>,
8452-
orFailWith: (a: A) => E2
8453-
): Effect<B, E | E2, R>
8454-
<A, E, R, E2>(self: Effect<A, E, R>, predicate: Predicate<A>, orFailWith: (a: A) => E2): Effect<A, E | E2, R>
8455-
<A, E, R, B extends A>(
8441+
predicate: Predicate<A> | Refinement<A, B>,
8442+
orFailWith: (a: EqualsWith<A, B, A, Exclude<A, B>>) => E2
8443+
): Effect<B, E2 | E, R>
8444+
<A, B extends A = A>(
8445+
predicate: Predicate<NoInfer<A>> | Refinement<NoInfer<A>, B>
8446+
): <E, R>(self: Effect<A, E, R>) => Effect<B, Cause.NoSuchElementException | E, R>
8447+
<A, E, R, B extends A = A>(
84568448
self: Effect<A, E, R>,
8457-
refinement: Refinement<A, B>
8449+
predicate: Predicate<A> | Refinement<A, B>
84588450
): Effect<B, E | Cause.NoSuchElementException, R>
8459-
<A, E, R>(self: Effect<A, E, R>, predicate: Predicate<A>): Effect<A, E | Cause.NoSuchElementException, R>
84608451
} = effect.filterOrFail
84618452

84628453
/**

packages/effect/src/Types.ts

+8
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,14 @@ export type Equals<X, Y> = (<T>() => T extends X ? 1 : 2) extends <
144144
>() => T extends Y ? 1 : 2 ? true
145145
: false
146146

147+
/**
148+
* Determines if two types are equal, allowing to specify the return types.
149+
*
150+
* @since 3.15.0
151+
* @category models
152+
*/
153+
export type EqualsWith<A, B, Y, N> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? Y : N
154+
147155
/**
148156
* Determines if a record contains any of the given keys.
149157
*

0 commit comments

Comments
 (0)