-
-
Notifications
You must be signed in to change notification settings - Fork 548
Array.flatten and pipe losing type information, inducing run time crash #5963
Description
What version of Effect is running?
3.19.13
What steps can reproduce the bug?
Not sure you can reproduce the bug with this but the situation is similar to the one I describe here, also I propose a solution at the end of this message.
declare const arg1: Effect.Effect<
void,
Error1 | Error2,
Requirement1 | Requirement2
>;
declare const arg2: Effect.Effect<
void,
Error1 | Error2,
Requirement1 | Requirement2 | Requirement3
>
const arg1BeforeArg2 = pipe(
[
arg1,
arg2
],
Array.flatten,
);
const arg2BeforeArg1 = pipe(
[
arg2,
arg1
],
Array.flatten,
);
What is the expected behavior?
Same signature for both arg1BeforeArg2 and arg2BeforeArg1
arg1BeforeArg2;
// ^
// Effect.Effect<
// void,
// Error1 | Error2,
// Requirement1 | Requirement2 | Requirement3
// >
arg2BeforeArg1;
// ^
// Effect.Effect<
// void,
// Error1 | Error2,
// Requirement1 | Requirement2 | Requirement3
// >
What do you see instead?
arg1BeforeArg2;
// ^
// Effect.Effect<
// void,
// Error1 | Error2,
// Requirement1 | Requirement2
// >
arg1BeforeArg2;
// ^
// Effect.Effect<
// void,
// Error1 | Error2,
// Requirement1 | Requirement2 | Requirement3
// >
The order of the items in the array shouldn't change the union type for Requirements.
Of course, this lead to a runtime error as we couldn't know that we were supposed to provide Requirement3 as the type system wasn't aware of it
Additional information
I was able to make it work by changing the type returned by the flatten function to be the one from this great library: https://github.com/inocan-group/inferred-types/blob/12da2b1282f2bab01f1e1d450aa87aa3bed85bcc/modules/types/src/lists/Flatten.ts
But it's really complex type 😅.
// packages/effect/src/Array.ts
import { Flatten } from 'inferred-types';
export const flatten: <const S extends ReadonlyArray<ReadonlyArray<any>>>(self: S) => Flatten<S> = flatMap(
identity
) as anyAlso by adding a const type parameter in pipe
// packages/effect/src/Pipeable.ts
export interface Pipeable {
pipe<const A>(this: A): A
pipe<const A, B = never>(this: A, ab: (_: A) => B): B
pipe<const A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C
...
}// packages/effect/src/Function.ts
export function pipe<const A>(a: A): A
export function pipe<const A, B = never>(a: A, ab: (a: A) => B): B
export function pipe<const A, B = never, C = never>(
a: A,
ab: (a: A) => B,
bc: (b: B) => C
): C
...with both those changes, the type is inferred correctly.