Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

disallow excess properties for various function options #4541

Merged
merged 1 commit into from
Mar 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/pretty-plants-taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@effect/typeclass": patch
"@effect/platform": patch
"effect": patch
---

Disallowed excess properties for various function options
32 changes: 16 additions & 16 deletions packages/effect/src/Effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -788,13 +788,13 @@ export const once: <A, E, R>(self: Effect<A, E, R>) => Effect<Effect<void, E, R>
*/
export const all: <
const Arg extends Iterable<Effect<any, any, any>> | Record<string, Effect<any, any, any>>,
O extends {
O extends NoExcessProperties<{
readonly concurrency?: Concurrency | undefined
readonly batching?: boolean | "inherit" | undefined
readonly discard?: boolean | undefined
readonly mode?: "default" | "validate" | "either" | undefined
readonly concurrentFinalizers?: boolean | undefined
}
}, O>
>(arg: Arg, options?: O) => All.Return<Arg, O> = fiberRuntime.all

/**
Expand Down Expand Up @@ -837,13 +837,13 @@ export const all: <
* @category Collecting
*/
export const allWith: <
O extends {
O extends NoExcessProperties<{
readonly concurrency?: Concurrency | undefined
readonly batching?: boolean | "inherit" | undefined
readonly discard?: boolean | undefined
readonly mode?: "default" | "validate" | "either" | undefined
readonly concurrentFinalizers?: boolean | undefined
}
}, O>
>(
options?: O
) => <const Arg extends Iterable<Effect<any, any, any>> | Record<string, Effect<any, any, any>>>(
Expand Down Expand Up @@ -937,13 +937,13 @@ export declare namespace All {
*/
export type Return<
Arg extends Iterable<EffectAny> | Record<string, EffectAny>,
O extends {
O extends NoExcessProperties<{
readonly concurrency?: Concurrency | undefined
readonly batching?: boolean | "inherit" | undefined
readonly discard?: boolean | undefined
readonly mode?: "default" | "validate" | "either" | undefined
readonly concurrentFinalizers?: boolean | undefined
}
}, O>
> = [Arg] extends [ReadonlyArray<EffectAny>] ? ReturnTuple<Arg, IsDiscard<O>, ExtractMode<O>>
: [Arg] extends [Iterable<EffectAny>] ? ReturnIterable<Arg, IsDiscard<O>, ExtractMode<O>>
: [Arg] extends [Record<string, EffectAny>] ? ReturnObject<Arg, IsDiscard<O>, ExtractMode<O>>
Expand Down Expand Up @@ -4181,7 +4181,7 @@ export declare namespace Retry {
* @since 2.0.0
* @category Error handling
*/
export type Return<R, E, A, O extends Options<E>> = Effect<
export type Return<R, E, A, O extends NoExcessProperties<Options<E>, O>> = Effect<
A,
| (O extends { schedule: Schedule.Schedule<infer _O, infer _I, infer _R> } ? E
: O extends { until: Refinement<E, infer E2> } ? E2
Expand Down Expand Up @@ -4325,15 +4325,15 @@ export declare namespace Retry {
* @category Error handling
*/
export const retry: {
<E, O extends Retry.Options<E>>(
<E, O extends NoExcessProperties<Retry.Options<E>, O>>(
options: O
): <A, R>(
self: Effect<A, E, R>
) => Retry.Return<R, E, A, O>
<B, E, R1>(
policy: Schedule.Schedule<B, NoInfer<E>, R1>
): <A, R>(self: Effect<A, E, R>) => Effect<A, E, R1 | R>
<A, E, R, O extends Retry.Options<E>>(
<A, E, R, O extends NoExcessProperties<Retry.Options<E>, O>>(
self: Effect<A, E, R>,
options: O
): Retry.Return<R, E, A, O>
Expand Down Expand Up @@ -7742,12 +7742,12 @@ export const bindAll: {
<
A extends object,
X extends Record<string, Effect<any, any, any>>,
O extends {
O extends NoExcessProperties<{
readonly concurrency?: Concurrency | undefined
readonly batching?: boolean | "inherit" | undefined
readonly mode?: "default" | "validate" | "either" | undefined
readonly concurrentFinalizers?: boolean | undefined
}
}, O>
>(
f: (a: NoInfer<A>) => [Extract<keyof X, keyof A>] extends [never] ? X : `Duplicate keys`,
options?: undefined | O
Expand All @@ -7763,12 +7763,12 @@ export const bindAll: {
<
A extends object,
X extends Record<string, Effect<any, any, any>>,
O extends {
O extends NoExcessProperties<{
readonly concurrency?: Concurrency | undefined
readonly batching?: boolean | "inherit" | undefined
readonly mode?: "default" | "validate" | "either" | undefined
readonly concurrentFinalizers?: boolean | undefined
},
}, O>,
E1,
R1
>(
Expand Down Expand Up @@ -9893,7 +9893,7 @@ export declare namespace Repeat {
* @since 2.0.0
* @category Repetition / Recursion
*/
export type Return<R, E, A, O extends Options<A>> = Effect<
export type Return<R, E, A, O extends NoExcessProperties<Options<A>, O>> = Effect<
(O extends { schedule: Schedule.Schedule<infer Out, infer _I, infer _R> } ? Out
: O extends { until: Refinement<A, infer B> } ? B
: A),
Expand Down Expand Up @@ -9979,15 +9979,15 @@ export declare namespace Repeat {
* @category Repetition / Recursion
*/
export const repeat: {
<O extends Repeat.Options<A>, A>(
<O extends NoExcessProperties<Repeat.Options<A>, O>, A>(
options: O
): <E, R>(
self: Effect<A, E, R>
) => Repeat.Return<R, E, A, O>
<B, A, R1>(
schedule: Schedule.Schedule<B, A, R1>
): <E, R>(self: Effect<A, E, R>) => Effect<B, E, R1 | R>
<A, E, R, O extends Repeat.Options<A>>(
<A, E, R, O extends NoExcessProperties<Repeat.Options<A>, O>>(
self: Effect<A, E, R>,
options: O
): Repeat.Return<R, E, A, O>
Expand Down
10 changes: 5 additions & 5 deletions packages/effect/src/Micro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import type { Predicate, Refinement } from "./Predicate.js"
import { hasProperty, isIterable, isTagged } from "./Predicate.js"
import type { Sink } from "./Sink.js"
import type { Stream } from "./Stream.js"
import type { Concurrency, Covariant, Equals, NotFunction, Simplify } from "./Types.js"
import type { Concurrency, Covariant, Equals, NoExcessProperties, NotFunction, Simplify } from "./Types.js"
import type * as Unify from "./Unify.js"
import { SingleShotGen, YieldWrap, yieldWrapGet } from "./Utils.js"

Expand Down Expand Up @@ -3778,10 +3778,10 @@ export declare namespace All {
*/
export type Return<
Arg extends Iterable<MicroAny> | Record<string, MicroAny>,
O extends {
O extends NoExcessProperties<{
readonly concurrency?: Concurrency | undefined
readonly discard?: boolean | undefined
}
}, O>
> = [Arg] extends [ReadonlyArray<MicroAny>] ? ReturnTuple<Arg, IsDiscard<O>>
: [Arg] extends [Iterable<MicroAny>] ? ReturnIterable<Arg, IsDiscard<O>>
: [Arg] extends [Record<string, MicroAny>] ? ReturnObject<Arg, IsDiscard<O>>
Expand All @@ -3799,10 +3799,10 @@ export declare namespace All {
*/
export const all = <
const Arg extends Iterable<Micro<any, any, any>> | Record<string, Micro<any, any, any>>,
O extends {
O extends NoExcessProperties<{
readonly concurrency?: Concurrency | undefined
readonly discard?: boolean | undefined
}
}, O>
>(arg: Arg, options?: O): All.Return<Arg, O> => {
if (Array.isArray(arg) || isIterable(arg)) {
return (forEach as any)(arg, identity, options)
Expand Down
7 changes: 5 additions & 2 deletions packages/effect/src/STM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import * as stm from "./internal/stm/stm.js"
import type * as Option from "./Option.js"
import type { Pipeable } from "./Pipeable.js"
import type { Predicate, Refinement } from "./Predicate.js"
import type { Covariant, MergeRecord, NoInfer } from "./Types.js"
import type { Covariant, MergeRecord, NoExcessProperties, NoInfer } from "./Types.js"
import type * as Unify from "./Unify.js"
import type { YieldWrap } from "./Utils.js"

Expand Down Expand Up @@ -230,7 +230,10 @@ export declare namespace All {
* @category utils
*/
export interface Signature {
<Arg extends ReadonlyArray<STMAny> | Iterable<STMAny> | Record<string, STMAny>, O extends Options>(
<
Arg extends ReadonlyArray<STMAny> | Iterable<STMAny> | Record<string, STMAny>,
O extends NoExcessProperties<Options, O>
>(
arg: Narrow<Arg>,
options?: O
): [Arg] extends [ReadonlyArray<STMAny>] ? ReturnTuple<Arg, IsDiscard<O>>
Expand Down
12 changes: 6 additions & 6 deletions packages/effect/src/internal/effect/circular.ts
Original file line number Diff line number Diff line change
Expand Up @@ -805,12 +805,12 @@ export const bindAll: {
<
A extends object,
X extends Record<string, Effect.Effect<any, any, any>>,
O extends {
O extends Types.NoExcessProperties<{
readonly concurrency?: Types.Concurrency | undefined
readonly batching?: boolean | "inherit" | undefined
readonly mode?: "default" | "validate" | "either" | undefined
readonly concurrentFinalizers?: boolean | undefined
}
}, O>
>(
f: (a: A) => [Extract<keyof X, keyof A>] extends [never] ? X : `Duplicate keys`,
options?: undefined | O
Expand All @@ -832,12 +832,12 @@ export const bindAll: {
<
A extends object,
X extends Record<string, Effect.Effect<any, any, any>>,
O extends {
O extends Types.NoExcessProperties<{
readonly concurrency?: Types.Concurrency | undefined
readonly batching?: boolean | "inherit" | undefined
readonly mode?: "default" | "validate" | "either" | undefined
readonly concurrentFinalizers?: boolean | undefined
},
}, O>,
E1,
R1
>(
Expand All @@ -864,12 +864,12 @@ export const bindAll: {
} = dual((args) => core.isEffect(args[0]), <
A extends object,
X extends Record<string, Effect.Effect<any, any, any>>,
O extends {
O extends Types.NoExcessProperties<{
readonly concurrency?: Types.Concurrency | undefined
readonly batching?: boolean | "inherit" | undefined
readonly mode?: "default" | "validate" | "either" | undefined
readonly concurrentFinalizers?: boolean | undefined
},
}, O>,
E1,
R1
>(
Expand Down
10 changes: 5 additions & 5 deletions packages/effect/src/internal/fiberRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import { currentScheduler, type Scheduler } from "../Scheduler.js"
import type * as Scope from "../Scope.js"
import type * as Supervisor from "../Supervisor.js"
import type * as Tracer from "../Tracer.js"
import type { Concurrency, NoInfer } from "../Types.js"
import type { Concurrency, NoExcessProperties, NoInfer } from "../Types.js"
import { internalCall, yieldWrapGet } from "../Utils.js"
import * as RequestBlock_ from "./blockedRequests.js"
import * as internalCause from "./cause.js"
Expand Down Expand Up @@ -1945,13 +1945,13 @@ const allEither = (
/* @internal */
export const all = <
const Arg extends Iterable<Effect.Effect<any, any, any>> | Record<string, Effect.Effect<any, any, any>>,
O extends {
O extends NoExcessProperties<{
readonly concurrency?: Concurrency | undefined
readonly batching?: boolean | "inherit" | undefined
readonly discard?: boolean | undefined
readonly mode?: "default" | "validate" | "either" | undefined
readonly concurrentFinalizers?: boolean | undefined
}
}, O>
>(
arg: Arg,
options?: O
Expand All @@ -1974,13 +1974,13 @@ export const all = <

/* @internal */
export const allWith = <
O extends {
O extends NoExcessProperties<{
readonly concurrency?: Concurrency | undefined
readonly batching?: boolean | "inherit" | undefined
readonly discard?: boolean | undefined
readonly mode?: "default" | "validate" | "either" | undefined
readonly concurrentFinalizers?: boolean | undefined
}
}, O>
>(options?: O) =>
<const Arg extends Iterable<Effect.Effect<any, any, any>> | Record<string, Effect.Effect<any, any, any>>>(
arg: Arg
Expand Down
8 changes: 4 additions & 4 deletions packages/effect/src/internal/schedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1810,14 +1810,14 @@ export const repeat_Effect = dual<

/** @internal */
export const repeat_combined = dual<{
<O extends Effect.Repeat.Options<A>, A>(
<O extends Types.NoExcessProperties<Effect.Repeat.Options<A>, O>, A>(
options: O
): <E, R>(self: Effect.Effect<A, E, R>) => Effect.Repeat.Return<R, E, A, O>
<B, A, R1>(
schedule: Schedule.Schedule<B, A, R1>
): <E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<B, E, R | R1>
}, {
<A, E, R, O extends Effect.Repeat.Options<A>>(
<A, E, R, O extends Types.NoExcessProperties<Effect.Repeat.Options<A>, O>>(
self: Effect.Effect<A, E, R>,
options: O
): Effect.Repeat.Return<R, E, A, O>
Expand Down Expand Up @@ -1907,15 +1907,15 @@ export const retry_Effect = dual<

/** @internal */
export const retry_combined: {
<E, O extends Effect.Retry.Options<E>>(
<E, O extends Types.NoExcessProperties<Effect.Retry.Options<E>, O>>(
options: O
): <A, R>(
self: Effect.Effect<A, E, R>
) => Effect.Retry.Return<R, E, A, O>
<B, E, R1>(
policy: Schedule.Schedule<B, Types.NoInfer<E>, R1>
): <A, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R1 | R>
<A, E, R, O extends Effect.Retry.Options<E>>(
<A, E, R, O extends Types.NoExcessProperties<Effect.Retry.Options<E>, O>>(
self: Effect.Effect<A, E, R>,
options: O
): Effect.Retry.Return<R, E, A, O>
Expand Down
5 changes: 3 additions & 2 deletions packages/effect/test/Effect/do-notation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as Either from "effect/Either"
import { pipe } from "effect/Function"
import * as Option from "effect/Option"
import * as Util from "effect/test/util"
import type { NoExcessProperties } from "effect/Types"

const expectRight = <R, L>(e: Effect.Effect<R, L>, expected: R) => {
Util.deepStrictEqual(Effect.runSync(Effect.either(e)), Either.right(expected))
Expand Down Expand Up @@ -48,7 +49,7 @@ describe("do notation", () => {

describe("bindAll", () => {
it("succeed", () => {
const getTest = <O extends { mode: "default" | "either" | "validate" }>(options: O) =>
const getTest = <O extends NoExcessProperties<{ mode: "default" | "either" | "validate" }, O>>(options: O) =>
Effect.Do.pipe(
Effect.bind("x", () => Effect.succeed(2)),
Effect.bindAll(({ x }) => ({
Expand Down Expand Up @@ -77,7 +78,7 @@ describe("do notation", () => {
})

it("with failure", () => {
const getTest = <O extends { mode: "default" | "either" | "validate" }>(options: O) =>
const getTest = <O extends NoExcessProperties<{ mode: "default" | "either" | "validate" }, O>>(options: O) =>
Effect.Do.pipe(
Effect.bind("x", () => Effect.succeed(2)),
Effect.bindAll(({ x }) => ({
Expand Down
18 changes: 13 additions & 5 deletions packages/platform/src/HttpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type * as Predicate from "effect/Predicate"
import type { Ref } from "effect/Ref"
import type * as Schedule from "effect/Schedule"
import type * as Scope from "effect/Scope"
import type { NoInfer } from "effect/Types"
import type { NoExcessProperties, NoInfer } from "effect/Types"
import type { Cookies } from "./Cookies.js"
import type * as Error from "./HttpClientError.js"
import type * as ClientRequest from "./HttpClientRequest.js"
Expand Down Expand Up @@ -478,7 +478,7 @@ export declare namespace Retry {
* @since 1.0.0
* @category error handling
*/
export type Return<R, E, O extends Effect.Retry.Options<E>> = HttpClient.With<
export type Return<R, E, O extends NoExcessProperties<Effect.Retry.Options<E>, O>> = HttpClient.With<
| (O extends { schedule: Schedule.Schedule<infer _O, infer _I, infer _R> } ? E
: O extends { until: Predicate.Refinement<E, infer E2> } ? E2
: E)
Expand All @@ -498,12 +498,20 @@ export declare namespace Retry {
* @category error handling
*/
export const retry: {
<E, O extends Effect.Retry.Options<E>>(options: O): <R>(self: HttpClient.With<E, R>) => Retry.Return<R, E, O>
<E, O extends NoExcessProperties<Effect.Retry.Options<E>, O>>(
options: O
): <R>(self: HttpClient.With<E, R>) => Retry.Return<R, E, O>
<B, E, R1>(
policy: Schedule.Schedule<B, NoInfer<E>, R1>
): <R>(self: HttpClient.With<E, R>) => HttpClient.With<E, R1 | R>
<E, R, O extends Effect.Retry.Options<E>>(self: HttpClient.With<E, R>, options: O): Retry.Return<R, E, O>
<E, R, B, R1>(self: HttpClient.With<E, R>, policy: Schedule.Schedule<B, E, R1>): HttpClient.With<E, R1 | R>
<E, R, O extends NoExcessProperties<Effect.Retry.Options<E>, O>>(
self: HttpClient.With<E, R>,
options: O
): Retry.Return<R, E, O>
<E, R, B, R1>(
self: HttpClient.With<E, R>,
policy: Schedule.Schedule<B, E, R1>
): HttpClient.With<E, R1 | R>
} = internal.retry

/**
Expand Down
Loading