Skip to content

Commit

Permalink
Merge pull request #4 from Mike96Angelo/v2-proposal
Browse files Browse the repository at this point in the history
V2 proposal
  • Loading branch information
Mike96Angelo committed Feb 25, 2023
2 parents ff6de51 + 758934b commit 5ae9dfb
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 105 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/npm-publish-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
node-version: 16
- run: npm ci
- run: npx tsc
- run: npm test
# - run: npm test

publish-npm:
needs: build
Expand Down
58 changes: 22 additions & 36 deletions optional-pair.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { Optional, toOptional, None } from "./optional.js";
import { Func } from "./util.types.js";
import { Func, nonNullable } from "./util.types.js";
import { Variant, variant, VariantTypeClass } from "./variant.js";

type OptionalPairVariants<A, B> =
type OptionalPairVariants<A extends nonNullable, B extends nonNullable> =
| Variant<"First", [first: A]>
| Variant<"Second", [second: B]>
| Variant<"Both", [first: A, second: B]>
| Variant<"Neither">;

type OptionalPairType<A, B> = OptionalPair<NonNullable<A>, NonNullable<B>>;

class OptionalPair<A, B> extends VariantTypeClass<OptionalPairVariants<A, B>> {
class OptionalPair<
A extends nonNullable,
B extends nonNullable
> extends VariantTypeClass<OptionalPairVariants<A, B>> {
first(): Optional<A> {
return this.match({
First(first) {
Expand Down Expand Up @@ -50,7 +51,9 @@ class OptionalPair<A, B> extends VariantTypeClass<OptionalPairVariants<A, B>> {
});
}

combineBoth<C>(combine: Func<[first: A, second: B], C>): Optional<C> {
combineBoth<C extends nonNullable>(
combine: Func<[first: A, second: B], C>
): Optional<C> {
return this.match({
Both(first, second) {
return toOptional(combine(first, second));
Expand All @@ -62,61 +65,51 @@ class OptionalPair<A, B> extends VariantTypeClass<OptionalPairVariants<A, B>> {
}
}

const Neither = new OptionalPair<any, any>(variant("Neither"));
const Neither = new OptionalPair<never, never>(variant("Neither"));

const First = <A, B>(
const First = <A extends nonNullable, B extends nonNullable>(
first: Optional<A>
): OptionalPair<NonNullable<A>, NonNullable<B>> =>
): OptionalPair<A, B> =>
first.match({
Some(value) {
return new OptionalPair<NonNullable<A>, NonNullable<B>>(
variant("First", value)
);
return new OptionalPair<A, B>(variant("First", value));
},
None() {
return Neither;
},
});

const Second = <A, B>(
const Second = <A extends nonNullable, B extends nonNullable>(
second: Optional<B>
): OptionalPair<NonNullable<A>, NonNullable<B>> =>
): OptionalPair<A, B> =>
second.match({
Some(value) {
return new OptionalPair<NonNullable<A>, NonNullable<B>>(
variant("Second", value)
);
return new OptionalPair<A, B>(variant("Second", value));
},
None() {
return Neither;
},
});

const Both = <A, B>(
const Both = <A extends nonNullable, B extends nonNullable>(
first: Optional<A>,
second: Optional<B>
): OptionalPair<NonNullable<A>, NonNullable<B>> =>
): OptionalPair<A, B> =>
first.match({
Some(first) {
return second.match({
Some(second) {
return new OptionalPair<NonNullable<A>, NonNullable<B>>(
variant("Both", first, second)
);
return new OptionalPair<A, B>(variant("Both", first, second));
},
None() {
return new OptionalPair<NonNullable<A>, NonNullable<B>>(
variant("First", first)
);
return new OptionalPair<A, B>(variant("First", first));
},
});
},
None() {
return second.match({
Some(second) {
return new OptionalPair<NonNullable<A>, NonNullable<B>>(
variant("Second", second)
);
return new OptionalPair<A, B>(variant("Second", second));
},
None() {
return Neither;
Expand All @@ -131,11 +124,4 @@ const toOptionalPair = <A, B>(
): OptionalPair<NonNullable<A>, NonNullable<B>> =>
Both(toOptional(first), toOptional(second));

export {
type OptionalPairType as OptionalPair,
Neither,
First,
Second,
Both,
toOptionalPair,
};
export { type OptionalPair, Neither, First, Second, Both, toOptionalPair };
78 changes: 44 additions & 34 deletions optional.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@
import { Err, Ok, Result } from "./result.js";
import { Func } from "./util.types.js";
import { nonNullable, Func } from "./util.types.js";
import { Variant, variant, VariantTypeClass } from "./variant.js";

type OptionalVariants<T> = Variant<"Some", [T]> | Variant<"None">;
type OptionalVariants<T extends nonNullable> =
| Variant<"Some", [T]>
| Variant<"None">;

type UnwrapOptional<T> = T extends Optional<infer K>
? K extends Optional<any>
? K extends Optional
? UnwrapOptional<K>
: K extends NonNullable<K>
? K
: never
: T extends NonNullable<T>
? T
: never;
: K
: T;

type OptionalType<T> = Optional<UnwrapOptional<T>>;

class Optional<T> extends VariantTypeClass<OptionalVariants<T>> {
class Optional<T extends nonNullable = nonNullable> extends VariantTypeClass<
OptionalVariants<T>
> {
/**
* Maps an `Optional<T>` to an `Optional<M>`.
*
* @param mapper - A function that maps `T` to `M`
* @returns An `Optional<M>`
*/
map<M extends Optional<any>>(mapper: Func<[value: T], M>): M;
map<M>(mapper: Func<[value: T], M>): Optional<NonNullable<M>>;
map<M>(mapper: Func<[value: T], M>) {
map<M extends Optional>(mapper: Func<[value: T], M>): M;
map<M extends nonNullable>(mapper: Func<[value: T], M>): Optional<M>;
map(mapper: Func<[any], any>) {
return this.match({
Some(value) {
return toOptional(mapper(value));
Expand Down Expand Up @@ -61,7 +59,15 @@ class Optional<T> extends VariantTypeClass<OptionalVariants<T>> {
* @param combiner - A function that combines `T` and `B` into `C`
* @returns `Optional<C>`
*/
combine<B, C>(b: Optional<B>, combiner: Func<[a: T, b: B], C>): Optional<C> {
combine<B extends nonNullable, C extends Optional>(
b: Optional<B>,
combiner: Func<[a: T, b: B], C>
): C;
combine<B extends nonNullable, C extends nonNullable>(
b: Optional<B>,
combiner: Func<[a: T, b: B], C>
): Optional<C>;
combine(b: Optional, combiner: Func<[any, any], any>) {
return this.map((a) => b.map((b) => combiner(a, b)));
}

Expand All @@ -75,9 +81,7 @@ class Optional<T> extends VariantTypeClass<OptionalVariants<T>> {
* @param error - function to compute `E` if this Optional in the None variant.
* @returns `Result<T, E>`
*/
toResult<E>(
error: () => NonNullable<E>
): Result<NonNullable<T>, NonNullable<E>> {
toResult<E extends nonNullable>(error: () => E): Result<T, E> {
return this.match({
Some(value) {
return Ok<T, E>(value!);
Expand All @@ -95,9 +99,9 @@ class Optional<T> extends VariantTypeClass<OptionalVariants<T>> {
* @param value - A value to store in the Some variant.
* @returns An Some variant
*/
function Some<T extends Optional<any>>(value: T): T;
function Some<T>(value: NonNullable<T>): OptionalType<T>;
function Some(value: any) {
function Some<T extends Optional>(value: T): T;
function Some<T extends nonNullable>(value: T): Optional<T>;
function Some(value: nonNullable) {
if (value == null) {
throw new TypeError(
"Some variant of Optional cannot be constructed with null or undefined"
Expand All @@ -114,7 +118,7 @@ function Some(value: any) {
/**
* None variant representing that their is no value.
*/
const None = new Optional<any>(variant("None"));
const None = new Optional<never>(variant("None"));

/**
* Converts a nullable type into an Optional variant.
Expand All @@ -124,7 +128,7 @@ const None = new Optional<any>(variant("None"));
* @param value - A nullable value.
* @returns An Optional variant.
*/
const toOptional = <T>(value: T): OptionalType<T> =>
const toOptional = <T>(value: T): Optional<NonNullable<T>> =>
value != null ? Some(value) : None;

/**
Expand All @@ -133,15 +137,15 @@ const toOptional = <T>(value: T): OptionalType<T> =>
* @param mapper - A mapping function `(A) => B`
* @returns `(Optional<A>) => Optional<B>`
*/
function OptionalMapper<A, B extends Optional<any>>(
function OptionalMapper<A extends nonNullable, B extends Optional>(
mapper: Func<[value: A], B>
): Func<[a: Optional<A>], B>;
function OptionalMapper<A, B>(
function OptionalMapper<A extends nonNullable, B extends nonNullable>(
mapper: Func<[value: A], B>
): Func<[a: Optional<A>], Optional<B>>;
function OptionalMapper(
mapper: Func<[any], any>
): Func<[value: Optional<any>], Optional<any>> {
): Func<[value: Optional], Optional> {
return (a) => a.map(mapper);
}

Expand All @@ -151,21 +155,27 @@ function OptionalMapper(
* @param combiner - A combining function `(A, B) => C`
* @returns `(Optional<A>, Optional<B>) => Optional<C>`
*/
function OptionalCombiner<A, B, C extends Optional<any>>(
combiner: Func<[a: A, b: B], C>
): Func<[a: Optional<A>, b: Optional<B>], C>;
function OptionalCombiner<A, B, C>(
function OptionalCombiner<
A extends nonNullable,
B extends nonNullable,
C extends Optional
>(combiner: Func<[a: A, b: B], C>): Func<[a: Optional<A>, b: Optional<B>], C>;
function OptionalCombiner<
A extends nonNullable,
B extends nonNullable,
C extends nonNullable
>(
combiner: Func<[a: A, b: B], C>
): Func<[a: Optional<A>, b: Optional<B>], Optional<C>>;
function OptionalCombiner(
combiner: Func<[any, any], any>
): Func<[a: Optional<any>, b: Optional<any>], Optional<any>> {
): Func<[a: Optional, b: Optional], Optional> {
return (a, b) => a.combine(b, combiner);
}

export {
type OptionalType as Optional,
UnwrapOptional,
type Optional,
type UnwrapOptional,
Some,
None,
toOptional,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "variant-match",
"version": "2.0.1",
"version": "2.0.0",
"description": "Brings variant match pattern to TypeScript.",
"type": "module",
"main": "mod.js",
Expand Down
Loading

0 comments on commit 5ae9dfb

Please sign in to comment.