-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: improve result nullish type #194
- Loading branch information
1 parent
0534326
commit b640159
Showing
11 changed files
with
371 additions
and
33 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
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 |
---|---|---|
@@ -0,0 +1,302 @@ | ||
diff --git a/lib/core/fail.ts b/lib/core/fail.ts | ||
index 8f0739d..239c95e 100644 | ||
--- a/lib/core/fail.ts | ||
+++ b/lib/core/fail.ts | ||
@@ -16,7 +16,7 @@ import Result from "./result"; | ||
* @argument P generic type for payload. | ||
* @default void as no state. | ||
*/ | ||
- function Fail(): Result<string, string, {}>; | ||
+ function Fail(): Result<null, string, {}>; | ||
|
||
/** | ||
* @description Create an instance of Result as failure state. | ||
@@ -33,7 +33,7 @@ import Result from "./result"; | ||
* @argument P generic type for payload. | ||
* @default void as no state. | ||
*/ | ||
-function Fail(): IResult<string, string, {}>; | ||
+function Fail(): IResult<null, string, {}>; | ||
|
||
/** | ||
* @description Create an instance of Result as failure state. | ||
@@ -50,7 +50,7 @@ function Fail(): IResult<string, string, {}>; | ||
* @argument P generic type for payload. | ||
* @default void as no state. | ||
*/ | ||
- function Fail<E, M extends {} = {}, P = void>(error: E extends void ? null : E, metaData?: M): Result<P, E extends void ? string : E, M>; | ||
+ function Fail<E, M extends {} = {}>(error: E extends void ? null : E, metaData?: M): Result<null, E extends void ? string : E, M>; | ||
|
||
|
||
/** | ||
@@ -68,7 +68,7 @@ function Fail(): IResult<string, string, {}>; | ||
* @argument P generic type for payload. | ||
* @default void as no state. | ||
*/ | ||
-function Fail<E, M extends {} = {}, P = void>(error: E extends void ? null : E, metaData?: M): IResult<P, E extends void ? string : E, M>; | ||
+function Fail<E, M extends {} = {}>(error: E extends void ? null : E, metaData?: M): IResult<null, E extends void ? string : E, M>; | ||
|
||
/** | ||
* @description Create an instance of Result as failure state. | ||
@@ -85,7 +85,7 @@ function Fail<E, M extends {} = {}, P = void>(error: E extends void ? null : E, | ||
* @argument P generic type for payload. | ||
* @default void as no state. | ||
*/ | ||
-function Fail<E = string, M extends {} = {}, P = void>(error?: E extends void ? null : E, metaData?: M): IResult<P, E extends void ? string : E, M> { | ||
+function Fail<E = string, M extends {} = {}>(error?: E extends void ? null : E, metaData?: M): IResult<null, E extends void ? string : E, M> { | ||
const _error = (typeof error !== 'undefined' && error !== null) ? error : 'void error. no message!'; | ||
return Result.fail(_error as any, metaData); | ||
} | ||
diff --git a/lib/core/result.ts b/lib/core/result.ts | ||
index 0e26f83..f65011d 100644 | ||
--- a/lib/core/result.ts | ||
+++ b/lib/core/result.ts | ||
@@ -29,7 +29,7 @@ export class Result<T = void, D = string, M = {}> implements IResult<T, D, M> { | ||
* @returns instance of Result<void>. | ||
*/ | ||
public static Ok(): Result<void>; | ||
- | ||
+ | ||
/** | ||
* @description Create an instance of Result as success state. | ||
* @returns instance of Result<void>. | ||
@@ -42,7 +42,7 @@ export class Result<T = void, D = string, M = {}> implements IResult<T, D, M> { | ||
* @param metaData as M to state. | ||
* @returns instance of Result. | ||
*/ | ||
- public static Ok<T, M = {}, D = string>(data: T, metaData?: M): Result<T, D, M>; | ||
+ public static Ok<T, M = {}, D = string>(data: T, metaData?: M): Result<T, D, M>; | ||
|
||
/** | ||
* @description Create an instance of Result as success state with data and metadata to payload. | ||
@@ -51,7 +51,7 @@ export class Result<T = void, D = string, M = {}> implements IResult<T, D, M> { | ||
* @returns instance of Result. | ||
*/ | ||
public static Ok<T, M = {}, D = string>(data: T, metaData?: M): IResult<T, D, M>; | ||
- | ||
+ | ||
/** | ||
* @description Create an instance of Result as success state with data and metadata to payload. | ||
* @param data as T to payload. | ||
@@ -70,7 +70,7 @@ export class Result<T = void, D = string, M = {}> implements IResult<T, D, M> { | ||
* @param metaData as M to state. | ||
* @returns instance of Result. | ||
*/ | ||
- public static fail<D = string, M = {}, T = void>(error?: D, metaData?: M): Result<T, D, M>; | ||
+ public static fail<D = string, M = {}>(error?: D, metaData?: M): Result<null, D, M>; | ||
|
||
/** | ||
* @description Create an instance of Result as failure state with error and metadata to payload. | ||
@@ -100,7 +100,7 @@ export class Result<T = void, D = string, M = {}> implements IResult<T, D, M> { | ||
*/ | ||
public static combine<A = any, B = any, M = any>(results: Array<IResult<any, any, any>>): IResult<A, B, M> { | ||
const iterator = Result.iterate(results); | ||
- if (iterator.isEmpty()) return Result.fail('No results provided on combine param' as B) as IResult<A, B, M>; | ||
+ if (iterator.isEmpty()) return Result.fail('No results provided on combine param' as B) as unknown as IResult<A, B, M>; | ||
while (iterator.hasNext()) { | ||
const currentResult = iterator.next(); | ||
if (currentResult.isFail()) return currentResult as IResult<A, B, M>; | ||
@@ -167,6 +167,27 @@ export class Result<T = void, D = string, M = {}> implements IResult<T, D, M> { | ||
isFail(): boolean { | ||
return this.#isFail; | ||
} | ||
+ /** | ||
+ * @description Determines if the result instance contains a `null` value. | ||
+ * This method is particularly useful when dealing with dynamically typed results, | ||
+ * allowing developers to validate and refine types based on the state of the result. | ||
+ * | ||
+ * @returns {boolean} | ||
+ * - `true` if the result instance holds a `null` value. | ||
+ * - `false` otherwise. | ||
+ * | ||
+ * @example | ||
+ * const result = Result.Ok(null); | ||
+ * | ||
+ * if (result.isNull()) { | ||
+ * console.log("The result value is null"); | ||
+ * } else { | ||
+ * console.log("The result value is not null:", result.value()); | ||
+ * } | ||
+ */ | ||
+ isNull(): boolean { | ||
+ return this.#data === null || this.#isFail; | ||
+ } | ||
|
||
/** | ||
* @description Check if result instance is success. | ||
diff --git a/lib/types.ts b/lib/types.ts | ||
index e3af3c0..59989ba 100644 | ||
--- a/lib/types.ts | ||
+++ b/lib/types.ts | ||
@@ -31,6 +31,7 @@ export interface IResult<T, D = string, M = {}> { | ||
value(): T; | ||
error(): D; | ||
isFail(): boolean; | ||
+ isNull(): boolean; | ||
isOk(): boolean; | ||
metaData(): M; | ||
toObject(): IResultObject<T, D, M>; | ||
@@ -196,7 +197,7 @@ export interface IPublicHistory<Props> { | ||
export type IPropsValidation<T> = { [P in keyof Required<T>]: (value: T[P]) => boolean }; | ||
|
||
export interface IAdapter<F, T, E = any, M = any> { | ||
- build(target: F): IResult<T, E, M>; | ||
+ build(target: F): IResult<T | null, E, M>; | ||
} | ||
|
||
export interface Adapter<A = any, B = any> { | ||
diff --git a/tests/core/adapter.spec.ts b/tests/core/adapter.spec.ts | ||
index 1fc9f5d..4d9e470 100644 | ||
--- a/tests/core/adapter.spec.ts | ||
+++ b/tests/core/adapter.spec.ts | ||
@@ -104,7 +104,7 @@ describe('adapter v1', () => { | ||
type Err = { err: string; stack?: string }; | ||
|
||
class CustomAdapter implements IAdapter<In, Out, Err> { | ||
- build(target: In): IResult<Out, Err> { | ||
+ build(target: In): IResult<Out | null, Err> { | ||
if (typeof target.a !== 'number') return Fail({ err: 'target.a is not a number' }); | ||
return Ok({ b: target.a.toString() }); | ||
} | ||
diff --git a/tests/core/aggregate.spec.ts b/tests/core/aggregate.spec.ts | ||
index 4fffe18..591f627 100644 | ||
--- a/tests/core/aggregate.spec.ts | ||
+++ b/tests/core/aggregate.spec.ts | ||
@@ -114,7 +114,7 @@ describe('aggregate', () => { | ||
return this.validator.number(value).isBetween(0, 130); | ||
} | ||
|
||
- public static create(props: Props): IResult<ValueObject<Props>> { | ||
+ public static create(props: Props): IResult<ValueObject<Props> | null> { | ||
if (!this.isValidValue(props.value)) return Result.fail('Invalid value'); | ||
return Result.Ok(new AgeVo(props)); | ||
} | ||
@@ -142,14 +142,14 @@ describe('aggregate', () => { | ||
super(props); | ||
} | ||
|
||
- public static create(props: AggProps): IResult<Aggregate<AggProps>> { | ||
+ public static create(props: AggProps): IResult<Aggregate<AggProps> | null> { | ||
return Result.Ok(new UserAgg(props)); | ||
} | ||
} | ||
|
||
it('should create a user with success', () => { | ||
|
||
- const age = AgeVo.create({ value: 21 }).value(); | ||
+ const age = AgeVo.create({ value: 21 }).value() as AgeVo; | ||
const user = UserAgg.create({ age }); | ||
|
||
expect(user.isOk()).toBeTruthy(); | ||
@@ -158,10 +158,10 @@ describe('aggregate', () => { | ||
|
||
it('should get value from age with success', () => { | ||
|
||
- const age = AgeVo.create({ value: 21 }).value(); | ||
+ const age = AgeVo.create({ value: 21 }).value() as AgeVo; | ||
const user = UserAgg.create({ age }).value(); | ||
|
||
- const result = user | ||
+ const result = (user as Aggregate<AggProps>) | ||
.get('age') | ||
.get('value'); | ||
|
||
diff --git a/tests/core/entity.spec.ts b/tests/core/entity.spec.ts | ||
index 2b49d7b..7b34e15 100644 | ||
--- a/tests/core/entity.spec.ts | ||
+++ b/tests/core/entity.spec.ts | ||
@@ -1,4 +1,4 @@ | ||
-import { Entity, Id, Ok, Result, ValueObject } from "../../lib/core"; | ||
+import { Entity, Fail, Id, Ok, Result, ValueObject } from "../../lib/core"; | ||
import { Adapter, IResult, UID } from "../../lib/types"; | ||
|
||
describe("entity", () => { | ||
@@ -16,7 +16,8 @@ describe("entity", () => { | ||
return value !== undefined; | ||
} | ||
|
||
- public static create(props: Props): IResult<EntitySample> { | ||
+ public static create(props: Props): IResult<EntitySample | null> { | ||
+ if(!props) return Fail('props is required') | ||
return Result.Ok(new EntitySample(props)) | ||
} | ||
} | ||
@@ -24,7 +25,7 @@ describe("entity", () => { | ||
it('should get prototype', () => { | ||
const ent = EntitySample.create({ foo: 'bar' }); | ||
|
||
- ent.value().change('foo', 'changed'); | ||
+ ent.value()?.change('foo', 'changed'); | ||
expect(ent.isOk()).toBeTruthy(); | ||
}); | ||
}); | ||
@@ -630,7 +631,7 @@ describe("entity", () => { | ||
return this.util.string(this.props.foo).removeSpaces(); | ||
} | ||
|
||
- public static create(props: Props): IResult<ValSamp> { | ||
+ public static create(props: Props): IResult<ValSamp | null> { | ||
const isValid = this.isValidProps(props.foo); | ||
if (!isValid) return Result.fail('Erro'); | ||
return Result.Ok(new ValSamp(props)) | ||
@@ -645,7 +646,7 @@ describe("entity", () => { | ||
it('should remove space from value', () => { | ||
const ent = ValSamp.create({ foo: ' Some Value With Spaces ' }); | ||
expect(ent.isOk()).toBeTruthy(); | ||
- expect(ent.value().RemoveSpace()).toBe('SomeValueWithSpaces'); | ||
+ expect(ent.value()?.RemoveSpace()).toBe('SomeValueWithSpaces'); | ||
}); | ||
}); | ||
|
||
diff --git a/tests/core/fail.spec.ts b/tests/core/fail.spec.ts | ||
index 97f79f3..9adf361 100644 | ||
--- a/tests/core/fail.spec.ts | ||
+++ b/tests/core/fail.spec.ts | ||
@@ -97,11 +97,7 @@ describe('fail', () => { | ||
arg: string; | ||
} | ||
|
||
- interface Payload { | ||
- user: any; | ||
- } | ||
- | ||
- const result = Fail<Generic, MetaData, Payload>({ message: 'invalid email' }, { arg: '[email protected]' }); | ||
+ const result = Fail<Generic, MetaData>({ message: 'invalid email' }, { arg: '[email protected]' }); | ||
expect(result.isOk()).toBeFalsy(); | ||
expect(result.isFail()).toBeTruthy(); | ||
expect(result.toObject()).toEqual({ | ||
@@ -116,7 +112,6 @@ describe('fail', () => { | ||
describe('generic types', () => { | ||
|
||
type Error = { message: string }; | ||
- type Payload = { data: { status: number } }; | ||
type MetaData = { args: number }; | ||
|
||
it('should fail generate the same payload as result', () => { | ||
@@ -125,10 +120,10 @@ describe('fail', () => { | ||
const metaData: MetaData = { args: status }; | ||
const error: Error = { message: 'something went wrong!' }; | ||
|
||
- const resultInstance = Result.fail<Error, MetaData, Payload>(error, metaData); | ||
- const okInstance = Fail<Error, MetaData, Payload>(error, metaData); | ||
+ const resultInstance = Result.fail<Error, MetaData>(error, metaData); | ||
+ const failInstance = Fail<Error, MetaData>(error, metaData); | ||
|
||
- expect(resultInstance.toObject()).toEqual(okInstance.toObject()); | ||
+ expect(resultInstance.toObject()).toEqual(failInstance.toObject()); | ||
|
||
}); | ||
|
||
diff --git a/tests/core/value-object.spec.ts b/tests/core/value-object.spec.ts | ||
index 1add011..5530694 100644 | ||
--- a/tests/core/value-object.spec.ts | ||
+++ b/tests/core/value-object.spec.ts | ||
@@ -328,7 +328,7 @@ describe('value-object', () => { | ||
return isValidAge && isValidDate; | ||
} | ||
|
||
- public static create(props: Props1): IResult<HumanAge> { | ||
+ public static create(props: Props1): IResult<HumanAge | null> { | ||
if (!HumanAge.isValidProps(props)) return Result.fail('Invalid props'); | ||
return Result.Ok(new HumanAge(props)); | ||
} |
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
Oops, something went wrong.