Skip to content

Commit 9f6409f

Browse files
feat: add Maybe.project() (#184)
1 parent 1849e78 commit 9f6409f

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

src/maybe/maybe.interface.ts

+5
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ export interface IMaybe<T> extends IMonad<T> {
108108
*/
109109
flatMapAuto<R>(fn: (v: NonNullable<T>) => R): IMaybe<NonNullable<R>>
110110

111+
/**
112+
* drill into Just values
113+
*/
114+
project<R extends T[keyof T]>(fn: (d: NonNullable<T>) => R): IMaybe<NonNullable<R>>
115+
111116
/**
112117
* Apply a predicate which if met, continues the Maybe chain,
113118
* otherwise return an empty Maybe

src/maybe/maybe.spec.ts

+47-1
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,52 @@ describe('Maybe', () => {
485485

486486
})
487487

488+
describe('chain', () => {
489+
it('should', () => {
490+
interface TestFace { thing: number; name: string }
491+
const obj: TestFace = { thing: 1, name: 'string' }
492+
const chained = maybe(obj)
493+
.project(a => a.name)
494+
.map(a => `${a} hello`)
495+
496+
expect(chained.isSome()).toEqual(true)
497+
expect(chained.valueOrUndefined()).toEqual('string hello')
498+
})
499+
500+
it('should', () => {
501+
const obj = { thing: 1, name: 'string', obj: { initial: 'PJM' } }
502+
const chained = maybe(obj)
503+
.project(a => a.obj)
504+
.project(a => a.initial)
505+
.map(a => `Hello, ${a}`)
506+
507+
expect(chained.isSome()).toEqual(true)
508+
expect(chained.valueOrUndefined()).toEqual('Hello, PJM')
509+
})
510+
511+
it('should', () => {
512+
interface TestFace { thing: number; name: string }
513+
const obj: TestFace = { thing: 1, name: undefined as unknown as string }
514+
const chained = maybe(obj)
515+
.project(a => a.name)
516+
.project(a => a)
517+
.map(a => `${a} hello`)
518+
519+
expect(chained.isNone()).toEqual(true)
520+
expect(chained.valueOrUndefined()).toBeUndefined()
521+
})
522+
523+
it('should', () => {
524+
const obj = undefined as unknown as { name: string }
525+
const chained = maybe(obj)
526+
.project(a => a.name)
527+
.map(a => `${a} hello`)
528+
529+
expect(chained.isNone()).toEqual(true)
530+
expect(chained.valueOrUndefined()).toBeUndefined()
531+
})
532+
})
533+
488534
describe('isSome', () => {
489535
it('false path', () => {
490536
const sut = undefined as boolean | undefined
@@ -559,7 +605,7 @@ describe('Maybe', () => {
559605
it('should return result object with success', () => {
560606
const hasSome = maybe('hi')
561607
const sut = hasSome.toResult(new Error('oops'))
562-
608+
563609
expect(sut.unwrap()).toEqual('hi')
564610
})
565611

src/maybe/maybe.ts

+4
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ export class Maybe<T> implements IMaybe<T> {
102102
: new Maybe<NonNullable<R>>(fn(this.value as NonNullable<T>) as NonNullable<R>)
103103
}
104104

105+
public project<R extends T[keyof T]>(fn: (d: NonNullable<T>) => R): IMaybe<NonNullable<R>> {
106+
return this.flatMapAuto(fn)
107+
}
108+
105109
public filter(fn: (f: NonNullable<T>) => boolean): IMaybe<T> {
106110
return this.isNone()
107111
? new Maybe<T>()

0 commit comments

Comments
 (0)