diff --git a/packages/effect-fc/src/Async.ts b/packages/effect-fc/src/Async.ts index 03b158d..1391973 100644 --- a/packages/effect-fc/src/Async.ts +++ b/packages/effect-fc/src/Async.ts @@ -37,8 +37,8 @@ export type AsyncProps = Omit export const AsyncPrototype: AsyncPrototype = Object.freeze({ [AsyncTypeId]: AsyncTypeId, - asFunctionComponent

( - this: Component.Component & Async, + asFunctionComponent

( + this: Component.Component & Async, runtimeRef: React.RefObject>>, ) { const Inner = (props: { readonly promise: Promise }) => React.use(props.promise) @@ -106,7 +106,7 @@ export const isAsync = (u: unknown): u is Async => Predicate.hasProperty(u, Asyn * ) * ``` */ -export const async = >( +export const async = ( self: T & ( "promise" extends keyof Component.Component.Props ? "The 'promise' prop name is restricted for Async components. Please rename the 'promise' prop to something else." @@ -118,7 +118,8 @@ export const async = >( Component.Component.Props & AsyncProps, Component.Component.Success, Component.Component.Error, - Component.Component.Context + Component.Component.Context, + Component.Component.DefaultSignature & AsyncProps, Component.Component.Success> > & Async ) => Object.setPrototypeOf( @@ -154,14 +155,14 @@ export const async = >( * ``` */ export const withOptions: { - & Async>( + ( options: Partial ): (self: T) => T - & Async>( + ( self: T, options: Partial, ): T -} = Function.dual(2, & Async>( +} = Function.dual(2, ( self: T, options: Partial, ): T => Object.setPrototypeOf( diff --git a/packages/effect-fc/src/Component.ts b/packages/effect-fc/src/Component.ts index fd515ec..14208ee 100644 --- a/packages/effect-fc/src/Component.ts +++ b/packages/effect-fc/src/Component.ts @@ -10,36 +10,44 @@ export type ComponentTypeId = typeof ComponentTypeId /** * Represents an Effect-based React Component that integrates the Effect system with React. */ -export interface Component

-extends ComponentPrototype, ComponentOptions { +export interface Component

+extends ComponentPrototype, ComponentOptions { new(_: never): Record readonly [ComponentTypeId]: ComponentTypeId readonly "~Props": P readonly "~Success": A readonly "~Error": E readonly "~Context": R + readonly "~Function": F readonly body: (props: P) => Effect.Effect } export declare namespace Component { - export type Props> = [T] extends [Component] ? P : never - export type Success> = [T] extends [Component] ? A : never - export type Error> = [T] extends [Component] ? E : never - export type Context> = [T] extends [Component] ? R : never + export type Default

= Component> + export type Any = Component - export type AsComponent> = Component, Success, Error, Context> + export type Signature = (props: any) => React.ReactNode + export type DefaultSignature

= (props: P) => A + + export type Props = [T] extends [Component] ? P : never + export type Success = [T] extends [Component] ? A : never + export type Error = [T] extends [Component] ? E : never + export type Context = [T] extends [Component] ? R : never + export type Function = [T] extends [Component] ? F : never + + export type AsComponent = Component, Success, Error, Context, Function> } -export interface ComponentPrototype

+export interface ComponentPrototype

extends Pipeable.Pipeable { readonly [ComponentTypeId]: ComponentTypeId - readonly use: Effect.Effect<(props: P) => A, never, Exclude> + readonly use: Effect.Effect> asFunctionComponent( runtimeRef: React.Ref>> - ): (props: P) => A + ): F setFunctionComponentName(f: React.FC

): void transformFunctionComponent(f: React.FC

): React.FC

@@ -51,20 +59,20 @@ export const ComponentPrototype: ComponentPrototype = Object.free get use() { return use(this) }, - asFunctionComponent

( - this: Component, + asFunctionComponent

( + this: Component, runtimeRef: React.RefObject>>, ) { - return (props: P) => Runtime.runSync(runtimeRef.current)( + return ((props: P) => Runtime.runSync(runtimeRef.current)( Effect.andThen( useScope([], this), scope => Effect.provideService(this.body(props), Scope.Scope, scope), ) - ) + )) }, - setFunctionComponentName

( - this: Component, + setFunctionComponentName

( + this: Component, f: React.FC

, ) { f.displayName = this.displayName ?? "Anonymous" @@ -73,8 +81,8 @@ export const ComponentPrototype: ComponentPrototype = Object.free transformFunctionComponent: identity, } as const) -const use = Effect.fnUntraced(function*

( - self: Component +const use = Effect.fnUntraced(function*

( + self: Component ) { // biome-ignore lint/style/noNonNullAssertion: React ref initialization const runtimeRef = React.useRef>>(null!) @@ -82,7 +90,7 @@ const use = Effect.fnUntraced(function*

Runtime.runSync(runtimeRef.current)(Effect.cachedFunction( (_services: readonly any[]) => Effect.sync(() => { - const f: React.FC

= self.asFunctionComponent(runtimeRef) + const f = self.asFunctionComponent(runtimeRef) self.setFunctionComponentName(f) return self.transformFunctionComponent(f) }), @@ -131,13 +139,13 @@ export const defaultOptions: ComponentOptions = { } -export const isComponent = (u: unknown): u is Component<{}, React.ReactNode, unknown, unknown> => Predicate.hasProperty(u, ComponentTypeId) +export const isComponent = (u: unknown): u is Component.Default<{}, React.ReactNode, unknown, unknown> => Predicate.hasProperty(u, ComponentTypeId) export declare namespace make { export type Gen = { >, A extends React.ReactNode, P extends {} = {}>( body: (props: P) => Generator - ): Component< + ): Component.Default< P, A, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never @@ -152,7 +160,7 @@ export declare namespace make { >, props: NoInfer

, ) => B, - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> >, A, B, C extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( @@ -164,7 +172,7 @@ export declare namespace make { props: NoInfer

, ) => B, b: (_: B, props: NoInfer

) => C, - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> >, A, B, C, D extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( @@ -177,7 +185,7 @@ export declare namespace make { ) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> >, A, B, C, D, E extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( @@ -191,7 +199,7 @@ export declare namespace make { b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => E, - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> >, A, B, C, D, E, F extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( @@ -206,7 +214,7 @@ export declare namespace make { c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => E, e: (_: E, props: NoInfer

) => F, - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> >, A, B, C, D, E, F, G extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( @@ -222,7 +230,7 @@ export declare namespace make { d: (_: D, props: NoInfer

) => E, e: (_: E, props: NoInfer

) => F, f: (_: F, props: NoInfer

) => G, - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> >, A, B, C, D, E, F, G, H extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( @@ -239,7 +247,7 @@ export declare namespace make { e: (_: E, props: NoInfer

) => F, f: (_: F, props: NoInfer

) => G, g: (_: G, props: NoInfer

) => H, - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> >, A, B, C, D, E, F, G, H, I extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( @@ -257,7 +265,7 @@ export declare namespace make { f: (_: F, props: NoInfer

) => G, g: (_: G, props: NoInfer

) => H, h: (_: H, props: NoInfer

) => I, - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> >, A, B, C, D, E, F, G, H, I, J extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( @@ -276,35 +284,35 @@ export declare namespace make { g: (_: G, props: NoInfer

) => H, h: (_: H, props: NoInfer

) => I, i: (_: I, props: NoInfer

) => J, - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> } export type NonGen = { , P extends {} = {}>( body: (props: P) => Eff - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> , A, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => Eff, - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> , A, B, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, b: (_: B, props: NoInfer

) => Eff, - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> , A, B, C, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => Eff, - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> , A, B, C, D, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => Eff, - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> , A, B, C, D, E, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, @@ -312,7 +320,7 @@ export declare namespace make { c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => E, e: (_: E, props: NoInfer

) => Eff, - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> , A, B, C, D, E, F, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, @@ -321,7 +329,7 @@ export declare namespace make { d: (_: D, props: NoInfer

) => E, e: (_: E, props: NoInfer

) => F, f: (_: F, props: NoInfer

) => Eff, - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> , A, B, C, D, E, F, G, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, @@ -331,7 +339,7 @@ export declare namespace make { e: (_: E, props: NoInfer

) => F, f: (_: F, props: NoInfer

) => G, g: (_: G, props: NoInfer

) => Eff, - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> , A, B, C, D, E, F, G, H, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, @@ -342,7 +350,7 @@ export declare namespace make { f: (_: F, props: NoInfer

) => G, g: (_: G, props: NoInfer

) => H, h: (_: H, props: NoInfer

) => Eff, - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> , A, B, C, D, E, F, G, H, I, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, @@ -354,7 +362,7 @@ export declare namespace make { g: (_: G, props: NoInfer

) => H, h: (_: H, props: NoInfer

) => I, i: (_: I, props: NoInfer

) => Eff, - ): Component>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> } } @@ -501,27 +509,20 @@ export const makeUntraced: ( ) export declare namespace withSignature { - export type Signature = (props: any) => React.ReactNode - - export type Result< - T extends Component, - F extends Signature, - > = Omit & { - readonly use: Effect.Effect, Scope.Scope>> - asFunctionComponent( - runtimeRef: React.Ref, Scope.Scope>>> - ): F - } + export type Result = ( + & Omit> + & Component, Component.Success, Component.Error, Component.Context, F> + ) } export const withSignature: { - (): >( + (): ( self: T ) => withSignature.Result - >( + ( self: T ): withSignature.Result -} = (self?: Component): any => self === undefined +} = (self?: Component.Any): any => self === undefined ? identity : self @@ -542,14 +543,14 @@ export const withSignature: { * ``` */ export const withOptions: { - >( + ( options: Partial ): (self: T) => T - >( + ( self: T, options: Partial, ): T -} = Function.dual(2, >( +} = Function.dual(2, ( self: T, options: Partial, ): T => Object.setPrototypeOf( @@ -595,19 +596,19 @@ export const withOptions: { * */ export const withRuntime: { -

( +

( context: React.Context>, - ): (self: Component>) => (props: P) => A -

( - self: Component>, + ): (self: Component, F>) => F +

( + self: Component, F>, context: React.Context>, - ): (props: P) => A -} = Function.dual(2,

( - self: Component, + ): F +} = Function.dual(2,

( + self: Component, context: React.Context>, ) => function WithRuntime(props: P) { return React.createElement( - Runtime.runSync(React.useContext(context))(self.use), + Runtime.runSync(React.useContext(context))(self.use) as React.FC

, props, ) }) diff --git a/packages/effect-fc/src/Memoized.ts b/packages/effect-fc/src/Memoized.ts index 804d59f..0c981c8 100644 --- a/packages/effect-fc/src/Memoized.ts +++ b/packages/effect-fc/src/Memoized.ts @@ -61,7 +61,7 @@ export const isMemoized = (u: unknown): u is Memoized => Predicate.hasP * ) * ``` */ -export const memoized = >( +export const memoized = ( self: T ): T & Memoized> => Object.setPrototypeOf( Object.assign(function() {}, self), @@ -96,14 +96,14 @@ export const memoized = >( * ``` */ export const withOptions: { - & Memoized>( + >( options: Partial>> ): (self: T) => T - & Memoized>( + >( self: T, options: Partial>>, ): T -} = Function.dual(2, & Memoized>( +} = Function.dual(2, >( self: T, options: Partial>>, ): T => Object.setPrototypeOf(