Creating a Generic Tagged Union with Effect and TypeScript
I'm trying to make a generic tagged union to represent values that are to be loaded from an external resource. This is the nongeneric version:
Here is my attempt to make
Is there a cleaner way to go about doing this?
import { Effect as E, Schema as S } from "effect";
type Loadable = typeof Loadable.Type
const Loadable = S.Union(
S.TaggedStruct("NotStarted", {}),
S.TaggedStruct("Loading", {}),
S.TaggedStruct("Loaded", {
value: S.Union(S.Number, S.String), // How to make this generic?
}),
);
const [NotStarted, Loading, Loaded] = Loadable.members;
const x = Loaded.make({ value: 1 });
const y = Loaded.make({ value: 'a' });
// const z = Loaded.make({ value: true }); // Currently not allowedimport { Effect as E, Schema as S } from "effect";
type Loadable = typeof Loadable.Type
const Loadable = S.Union(
S.TaggedStruct("NotStarted", {}),
S.TaggedStruct("Loading", {}),
S.TaggedStruct("Loaded", {
value: S.Union(S.Number, S.String), // How to make this generic?
}),
);
const [NotStarted, Loading, Loaded] = Loadable.members;
const x = Loaded.make({ value: 1 });
const y = Loaded.make({ value: 'a' });
// const z = Loaded.make({ value: true }); // Currently not allowedHere is my attempt to make
LoadedLoaded generic:import { Match, Schema as S } from "effect";
const NotStarted = S.TaggedStruct("NotStarted", {});
const Loading = S.TaggedStruct("Loading", {});
const Loaded = <T>(schema: S.Schema<T>) =>
S.TaggedStruct("Loaded", {
value: schema,
});
type Loadable<T> =
| typeof NotStarted.Type
| typeof Loading.Type
| {
_tag: "Loaded";
value: T;
};
const Loadable = <T>(schema: S.Schema<T>) =>
S.Union(NotStarted, Loading, Loaded(schema))
const get = <T>(loadable: Loadable<T>) =>
Match.value(loadable).pipe(
Match.tag("Loaded", ({ value }) => value),
Match.orElse(() => null),
);
const x = Loaded(S.Int).make({ value: 1 });
const y = Loaded(S.String).make({ value: "a" });
const z = Loaded(S.Boolean).make({ value: true });
// All are correctly inferred
const shouldBeNumberOrNull = get(x);
const shouldBeStringOrNull = get(y);
const shouldBeBooleanOrNull = get(z);import { Match, Schema as S } from "effect";
const NotStarted = S.TaggedStruct("NotStarted", {});
const Loading = S.TaggedStruct("Loading", {});
const Loaded = <T>(schema: S.Schema<T>) =>
S.TaggedStruct("Loaded", {
value: schema,
});
type Loadable<T> =
| typeof NotStarted.Type
| typeof Loading.Type
| {
_tag: "Loaded";
value: T;
};
const Loadable = <T>(schema: S.Schema<T>) =>
S.Union(NotStarted, Loading, Loaded(schema))
const get = <T>(loadable: Loadable<T>) =>
Match.value(loadable).pipe(
Match.tag("Loaded", ({ value }) => value),
Match.orElse(() => null),
);
const x = Loaded(S.Int).make({ value: 1 });
const y = Loaded(S.String).make({ value: "a" });
const z = Loaded(S.Boolean).make({ value: true });
// All are correctly inferred
const shouldBeNumberOrNull = get(x);
const shouldBeStringOrNull = get(y);
const shouldBeBooleanOrNull = get(z);Is there a cleaner way to go about doing this?
