1/**
2 * Defines Effect's type-level unification protocol.
3 *
4 * Unification collapses unions of protocol-enabled values into their public data
5 * types. It is mostly for maintainers of Effect data types and advanced library
6 * authors; application code usually benefits from it through APIs such as
7 * `Effect`, `Option`, `Result`, `Stream`, `Layer`, and `Match`. This module
8 * exports the protocol symbols, the `Unify` type that performs normalization,
9 * and `unify`, an identity function that changes only the inferred type.
10 *
11 * @since 2.0.0
12 */
13
14import { identity } from "./Function.ts"
15
16/**
17 * Defines the unique symbol used to identify unification behavior in Effect types.
18 *
19 * **When to use**
20 *
21 * Use to define the widened type produced by the `Unify` protocol for a custom
22 * protocol-enabled data type.
23 *
24 * **Details**
25 *
26 * This symbol is a type-level protocol key. It describes how a protocol-enabled
27 * type widens during unification and has no runtime behavior.
28 *
29 * @see {@link typeSymbol} for storing the source type information used during unification
30 * @see {@link ignoreSymbol} for excluding protocol entries from unification
31 *
32 * @category symbols
33 * @since 2.0.0
34 */
35export declare const unifySymbol: unique symbol
36
37/**
38 * The type of the unifySymbol.
39 *
40 * **When to use**
41 *
42 * Use to reference the unification behavior property key in type-level
43 * protocol definitions.
44 *
45 * **Details**
46 *
47 * This type represents the unique symbol used for identifying unification
48 * behavior in Effect types. It's typically used in type-level operations
49 * to enable automatic type unification.
50 *
51 * @category symbols
52 * @since 2.0.0
53 */
54export type unifySymbol = typeof unifySymbol
55
56/**
57 * Defines the unique symbol used to identify the type information for unification.
58 *
59 * **When to use**
60 *
61 * Use when you need a type-level protocol key that exposes the source type
62 * read by `Unify` from a protocol-enabled data type.
63 *
64 * **Details**
65 *
66 * This symbol is a type-level protocol key. It stores the source type that
67 * unification reads when widening protocol-enabled values.
68 *
69 * @see {@link unifySymbol} for defining how protocol entries widen
70 *
71 * @category symbols
72 * @since 2.0.0
73 */
74export declare const typeSymbol: unique symbol
75
76/**
77 * The type of the typeSymbol.
78 *
79 * **When to use**
80 *
81 * Use to reference the type information property key in type-level protocol
82 * definitions.
83 *
84 * **Details**
85 *
86 * This type represents the unique symbol used for storing type information
87 * in types that support unification. It's used in type-level operations
88 * to access and manipulate type information.
89 *
90 * @category symbols
91 * @since 2.0.0
92 */
93export type typeSymbol = typeof typeSymbol
94
95/**
96 * Defines the unique symbol used to specify types that should be ignored during unification.
97 *
98 * **When to use**
99 *
100 * Use to hide helper protocol entries from `Unify` when they should not
101 * contribute to the widened type.
102 *
103 * **Details**
104 *
105 * This symbol is a type-level protocol key. It lists protocol entries that
106 * unification should ignore when computing the widened type.
107 *
108 * @see {@link unifySymbol} for defining the protocol entries being filtered
109 *
110 * @category symbols
111 * @since 2.0.0
112 */
113export declare const ignoreSymbol: unique symbol
114
115/**
116 * The type of the ignoreSymbol.
117 *
118 * **When to use**
119 *
120 * Use to reference the ignored-property key in type-level protocol
121 * definitions.
122 *
123 * **Details**
124 *
125 * This type represents the unique symbol used for marking types that should
126 * be ignored during unification operations. It's used in type-level operations
127 * to exclude specific types from the unification process.
128 *
129 * @category symbols
130 * @since 2.0.0
131 */
132export type ignoreSymbol = typeof ignoreSymbol
133
134type MaybeReturn<F> = F extends () => infer R ? R : NonNullable<F>
135
136type Keys<X extends [any, any]> = X extends [infer A, infer Ignore] ? Exclude<keyof A, Ignore>
137 : never
138
139type Values<X extends [any, any]> = X extends [infer A, infer Ignore]
140 ? Keys<[A, Ignore]> extends infer K ? K extends keyof A ? MaybeReturn<A[K]> : never : never
141 : never
142
143type Ignore<X> = X extends { [ignoreSymbol]?: infer Obj } ? keyof NonNullable<Obj>
144 : never
145
146type ExtractTypes<
147 X
148> = X extends {
149 [typeSymbol]?: infer _Type
150 [unifySymbol]?: infer _Unify
151} ? [NonNullable<_Unify>, Ignore<X>]
152 : never
153
154type FilterIn<A> = A extends any ? typeSymbol extends keyof A ? A : never : never
155
156type FilterInUnmatched<A, K> = A extends any
157 ? typeSymbol extends keyof A
158 ? A extends { [unifySymbol]?: infer U } ? [Extract<keyof NonNullable<U>, K>] extends [never] ? A : never
159 : A
160 : never
161 : never
162
163type FilterOut<A> = A extends any ? typeSymbol extends keyof A ? never : A : never
164
165/**
166 * Unifies types that implement the unification protocol.
167 *
168 * **When to use**
169 *
170 * Use to normalize unions of types that expose Effect's unification protocol.
171 *
172 * **Details**
173 *
174 * This type performs automatic type unification for types that contain
175 * the unification symbols (`unifySymbol`, `typeSymbol`, `ignoreSymbol`).
176 * It's primarily used internally by the Effect type system to handle
177 * complex type unions and provide better type inference.
178 *
179 * **Example** (Unifying protocol types)
180 *
181 * ```ts import.meta.vitest
182 * import type { Unify } from "effect"
183 *
184 * // Example of types that can be unified
185 * type UnifiableA = {
186 * value: string
187 * [Unify.typeSymbol]?: string
188 * [Unify.unifySymbol]?: { String: () => string }
189 * }
190 *
191 * type UnifiableB = {
192 * value: number
193 * [Unify.typeSymbol]?: number
194 * [Unify.unifySymbol]?: { Number: () => number }
195 * }
196 *
197 * // Unify automatically handles the union
198 * type Unified = Unify.Unify<UnifiableA | UnifiableB>
199 *
200 * const witness: Unified = "value"
201 * ```
202 *
203 * @see {@link unify} for applying this normalization to a value or function
204 *
205 * @category models
206 * @since 2.0.0
207 */
208export type Unify<A> = Values<
209 ExtractTypes<
210 (
211 & FilterIn<A>
212 & { [typeSymbol]: A }
213 )
214 >
215> extends infer Z ?
216 | Z
217 | FilterInUnmatched<
218 A,
219 Keys<
220 ExtractTypes<
221 (
222 & FilterIn<A>
223 & { [typeSymbol]: A }
224 )
225 >
226 >
227 >
228 | FilterOut<A>
229 : never
230
231/**
232 * Applies `Unify` to a value or function return type at compile time.
233 *
234 * **When to use**
235 *
236 * Use to keep a value or function unchanged at runtime while normalizing its
237 * inferred type with Effect's unification protocol.
238 *
239 * **Details**
240 *
241 * This is an identity function at runtime. For functions, the returned function
242 * has the same runtime behavior while its return type is normalized with the
243 * Effect unification protocol.
244 *
245 * **Example** (Unifying values and function results)
246 *
247 * ```ts import.meta.vitest
248 * import { Unify } from "effect"
249 *
250 * // Unify a simple value
251 * const unifiedValue = Unify.unify("hello") // => "hello"
252 * // Type: string
253 *
254 * // Unify a function result
255 * const createValue = () => ({ value: "test" })
256 *
257 * const unifiedFunction = Unify.unify(createValue)
258 * unifiedFunction().value // => "test"
259 *
260 * // Unify with curried functions
261 * const curriedFunction = (a: string) => (b: number) => ({ result: a + b })
262 * const unifiedCurried = Unify.unify(curriedFunction)
263 * // Type: (a: string) => (b: number) => Unify<{ result: string }>
264 * unifiedCurried("value-")(1).result // => "value-1"
265 * ```
266 *
267 * @see {@link Unify} for the type-level normalization applied by this helper
268 *
269 * @category utility types
270 * @since 2.0.0
271 */
272export const unify: {
273 <
274 Args extends Array<any>,
275 Args2 extends Array<any>,
276 Args3 extends Array<any>,
277 Args4 extends Array<any>,
278 Args5 extends Array<any>,
279 T
280 >(
281 x: (...args: Args) => (...args: Args2) => (...args: Args3) => (...args: Args4) => (...args: Args5) => T
282 ): (...args: Args) => (...args: Args2) => (...args: Args3) => (...args: Args4) => (...args: Args5) => Unify<T>
283 <
284 Args extends Array<any>,
285 Args2 extends Array<any>,
286 Args3 extends Array<any>,
287 Args4 extends Array<any>,
288 T
289 >(
290 x: (...args: Args) => (...args: Args2) => (...args: Args3) => (...args: Args4) => T
291 ): (...args: Args) => (...args: Args2) => (...args: Args3) => (...args: Args4) => Unify<T>
292 <
293 Args extends Array<any>,
294 Args2 extends Array<any>,
295 Args3 extends Array<any>,
296 T
297 >(
298 x: (...args: Args) => (...args: Args2) => (...args: Args3) => T
299 ): (...args: Args) => (...args: Args2) => (...args: Args3) => Unify<T>
300 <
301 Args extends Array<any>,
302 Args2 extends Array<any>,
303 T
304 >(
305 x: (...args: Args) => (...args: Args2) => T
306 ): (...args: Args) => (...args: Args2) => Unify<T>
307 <
308 Args extends Array<any>,
309 T
310 >(x: (...args: Args) => T): (...args: Args) => Unify<T>
311 <T>(x: T): Unify<T>
312} = identity as any