-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from vojtatranta/array-methods
Array methods
- Loading branch information
Showing
3 changed files
with
211 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,25 @@ | ||
name: Test workflow | ||
on: | ||
push: | ||
branches: | ||
- master | ||
branches: ["*"] | ||
|
||
jobs: | ||
test-job: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v3 | ||
- name: Checkout | ||
uses: actions/checkout@v3 | ||
|
||
- name: Use Node 17.x | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: '17.x' | ||
- name: Use Node 17.x | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: "17.x" | ||
|
||
- name: Install dependencies | ||
run: npm ci | ||
- name: Install dependencies | ||
run: npm ci | ||
|
||
- name: Test | ||
run: npm test | ||
- name: Test | ||
run: npm test | ||
|
||
- name: Types | ||
run: npm run typecheck | ||
- name: Types | ||
run: npm run typecheck |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,84 +1,126 @@ | ||
type NonNullableMaybe<T, D = never> = T extends Maybe<infer U> ? U extends null ? D : Maybe<U> : D; | ||
type NonNullablePart<T> = T extends null ? never : T | ||
type UnpackedMaybe<T> = T extends Maybe<infer U> ? U : T | ||
type NonNullableMaybe<T, D = never> = T extends Maybe<infer U> | ||
? U extends null | ||
? D | ||
: Maybe<U> | ||
: D; | ||
type NonNullablePart<T> = T extends null ? never : T; | ||
type UnpackedMaybe<T> = T extends Maybe<infer U> ? U : T; | ||
type Last<T extends any[] | readonly any[]> = T extends [...infer Rest, infer L] | ||
? L | ||
: never; | ||
|
||
export class Maybe<T> { | ||
private value: T | null | undefined | ||
private value: T | null | undefined; | ||
|
||
constructor(value: T | null | undefined) { | ||
this.value = value | ||
this.value = value; | ||
} | ||
|
||
static of<NV>(value: NV | null | undefined) { | ||
return new Maybe<NV>(value) | ||
return new Maybe<NV>(value); | ||
} | ||
|
||
static asMaybeInput<NV, R>(callback: (input: Maybe<NV>) => R): (externalInput: NV) => R { | ||
return (externalInput: NV): R => callback(Maybe.of<NV>(externalInput)) | ||
static asMaybeInput<NV, R>( | ||
callback: (input: Maybe<NV>) => R | ||
): (externalInput: NV) => R { | ||
return (externalInput: NV): R => callback(Maybe.of<NV>(externalInput)); | ||
} | ||
|
||
static asInput<NV, R>(callback: (input: NonNullable<NV>) => R): (externalInput: NV) => Maybe<R> { | ||
return (externalInput: NV): Maybe<R> => Maybe.of<NV>(externalInput).map(callback) | ||
static asInput<NV, R>( | ||
callback: (input: NonNullablePart<NV>) => R | ||
): (externalInput: NV) => Maybe<R> { | ||
return (externalInput: NV): Maybe<R> => | ||
Maybe.of<NV>(externalInput).map(callback); | ||
} | ||
|
||
static all<T extends unknown[]>(maybies: [...T]): Maybe<{ -readonly [P in keyof T]: UnpackedMaybe<T[P]> }> { | ||
const results: unknown[] = [] | ||
static all<T extends unknown[]>( | ||
maybies: [...T] | ||
): Maybe<{ -readonly [P in keyof T]: UnpackedMaybe<T[P]> }> { | ||
const results: unknown[] = []; | ||
|
||
const allMaybies = maybies.map((value) => value instanceof Maybe ? value : Maybe.of(value)) | ||
allMaybies.forEach((maybeValue) => maybeValue.map((truthyValue) => { | ||
results.push(truthyValue) | ||
})) | ||
const allMaybies = maybies.map((value) => | ||
value instanceof Maybe ? value : Maybe.of(value) | ||
); | ||
allMaybies.forEach((maybeValue) => | ||
maybeValue.map((truthyValue) => { | ||
results.push(truthyValue); | ||
}) | ||
); | ||
|
||
if (results.length === maybies.length) { | ||
return Maybe.of(results) as Maybe<{ -readonly [P in keyof T]: UnpackedMaybe<T[P]> }> | ||
return Maybe.of(results) as Maybe<{ | ||
-readonly [P in keyof T]: UnpackedMaybe<T[P]>; | ||
}>; | ||
} | ||
|
||
return Maybe.of(null as unknown as { -readonly [P in keyof T]: UnpackedMaybe<T[P]> }) | ||
return Maybe.of( | ||
null as unknown as { -readonly [P in keyof T]: UnpackedMaybe<T[P]> } | ||
); | ||
} | ||
|
||
map<R>(mapper: (value: NonNullable<T>) => R): Maybe<NonNullablePart<R>> { | ||
static fromFirst<T extends any[] | readonly any[]>( | ||
someArray: T | null | undefined | ||
): Maybe<T[0]> { | ||
return Maybe.fromNth<T, 0>(someArray, 0); | ||
} | ||
|
||
static fromLast<T extends any[] | readonly any[]>( | ||
someArray: T | null | undefined | ||
): Maybe<Last<T>> { | ||
return Maybe.fromNth(someArray, (someArray?.length ?? 0) - 1); | ||
} | ||
|
||
static fromNth<T extends any[] | readonly any[], N extends number>( | ||
someArray: T | null | undefined, | ||
n: N | ||
): Maybe<T[N]> { | ||
return Maybe.of<T[N]>(someArray?.[n]); | ||
} | ||
|
||
map<R>(mapper: (value: NonNullablePart<T>) => R): Maybe<NonNullablePart<R>> { | ||
if (this.value != null) { | ||
return new Maybe<NonNullablePart<R>>( | ||
mapper(this.value) as NonNullablePart<R> | ||
) | ||
mapper(this.value as NonNullablePart<T>) as NonNullablePart<R> | ||
); | ||
} | ||
|
||
return new Maybe<NonNullablePart<R>>(null) | ||
return new Maybe<NonNullablePart<R>>(null); | ||
} | ||
|
||
// NOTE: flatMap() "eats" the incomming maybe and will use the value of the current maybe as default | ||
// of the maybe returned by the Mapper() | ||
// you can safely use Maybies in the flatMap() function return value as if it was a direct value | ||
flatMap<U>( | ||
mapper: (value: NonNullable<T>) => U | ||
mapper: (value: NonNullablePart<T>) => U | ||
): NonNullableMaybe<U extends Maybe<infer R> ? Maybe<R> : Maybe<U>> { | ||
type LocalReturn = NonNullableMaybe<U extends Maybe<infer R> ? Maybe<R> : Maybe<U>> | ||
type LocalReturn = NonNullableMaybe< | ||
U extends Maybe<infer R> ? Maybe<R> : Maybe<U> | ||
>; | ||
|
||
if (this.value != null) { | ||
const result = mapper(this.value) | ||
const result = mapper(this.value as NonNullablePart<T>); | ||
|
||
if (result instanceof Maybe) { | ||
return new Maybe<U>(result.getValue(this.value)) as LocalReturn | ||
return new Maybe<U>(result.getValue(this.value)) as LocalReturn; | ||
} | ||
|
||
return new Maybe<U>(result) as LocalReturn | ||
return new Maybe<U>(result) as LocalReturn; | ||
} | ||
|
||
return new Maybe<U>(null) as LocalReturn | ||
return new Maybe<U>(null) as LocalReturn; | ||
} | ||
|
||
getValue(): T | null | undefined | ||
getValue<D>(defaultValue: D): NonNullable<T> | D | ||
getValue(): T | null | undefined; | ||
getValue<D>(defaultValue: D): NonNullablePart<T> | D; | ||
getValue<D>(defaultValue?: D): T | D | null | undefined { | ||
if (this.value) { | ||
return this.value | ||
return this.value; | ||
} | ||
|
||
if (typeof defaultValue !== 'undefined') { | ||
return defaultValue | ||
if (typeof defaultValue !== "undefined") { | ||
return defaultValue; | ||
} | ||
return this.value | ||
|
||
return this.value; | ||
} | ||
} | ||
|