From 6acf9f8918e715ba40dc646bb989c9fd18188f7c Mon Sep 17 00:00:00 2001 From: Hannes Leutloff Date: Fri, 23 Jul 2021 09:06:48 +0200 Subject: [PATCH 1/2] chore: Add tests regarding type narrowing expectations. --- test/unit/ResultTests.ts | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/test/unit/ResultTests.ts b/test/unit/ResultTests.ts index 817b0ed..1a887e5 100644 --- a/test/unit/ResultTests.ts +++ b/test/unit/ResultTests.ts @@ -84,6 +84,17 @@ suite('Result', (): void => { const newResult: Result<{ something: 'elseEntirely' }, Error> = result; } }); + + test('extrapolates value type correctly.', async (): Promise => { + const result = getValue(); + + if (result.hasError()) { + return; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const containedValue: Value = result.value; + }); }); suite('hasValue', (): void => { @@ -115,6 +126,17 @@ suite('Result', (): void => { const newResult: Result = result; } }); + + test('extrapolates error type correctly.', async (): Promise => { + const result = getError(); + + if (result.hasValue()) { + return; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const containedError = result.error; + }); }); suite('unwrapOrThrow', (): void => { @@ -252,7 +274,7 @@ suite('Result', (): void => { public someProp = 0; } - // This function compiling it enough for this test. + // This function compiling is enough for this test. // eslint-disable-next-line @typescript-eslint/no-unused-vars const someFunction = function (someResult: Result): Result { if (someResult.hasError()) { @@ -271,7 +293,7 @@ suite('Result', (): void => { public someOtherProp = 'some string'; } - // This function compiling it enough for this test. + // This function compiling is enough for this test. // eslint-disable-next-line @typescript-eslint/no-unused-vars const someFunction = function (someResult: Result): Result { if (someResult.hasValue()) { From 0199b773ac42a4289e4755b909bf85aeb10fef4d Mon Sep 17 00:00:00 2001 From: Hannes Leutloff Date: Fri, 23 Jul 2021 09:12:28 +0200 Subject: [PATCH 2/2] fix: Remove type information discarding. This lead to probably unfixable type collisions, so the feature must go. --- lib/Result.ts | 22 ++++++++-------- test/unit/ResultTests.ts | 57 ---------------------------------------- 2 files changed, 11 insertions(+), 68 deletions(-) diff --git a/lib/Result.ts b/lib/Result.ts index 86a5f49..6f0cc23 100644 --- a/lib/Result.ts +++ b/lib/Result.ts @@ -1,17 +1,17 @@ interface ResultBase { - hasError: () => this is ResultError & ResultBase; - hasValue: () => this is ResultValue & ResultBase; + hasError: () => this is ResultError; + hasValue: () => this is ResultValue; unwrapOrThrow: (errorTransformer?: (err: TError) => Error) => TValue; unwrapOrElse: (handleError: (error: Error) => TValue) => TValue; unwrapOrDefault: (defaultValue: TValue) => TValue; } -interface ResultError { +interface ResultError extends ResultBase { error: TError; } -const error = function (err: TError): ResultError & ResultBase { +const error = function (err: TError): ResultError { return { hasError (): boolean { return true; @@ -26,24 +26,24 @@ const error = function (err: TError): ResultError // eslint-disable-next-line @typescript-eslint/no-throw-literal throw err; }, - unwrapOrElse (handleError: (error: TError) => TValue): TValue { + unwrapOrElse (handleError: (error: TError) => TValue): TValue { return handleError(err); }, - unwrapOrDefault (defaultValue: TValue): TValue { + unwrapOrDefault (defaultValue: TValue): TValue { return defaultValue; }, error: err }; }; -interface ResultValue { +interface ResultValue extends ResultBase { value: TValue; } const value: { - (): ResultValue & ResultBase; - (value: TValue): ResultValue & ResultBase; -} = function (val?: TValue): ResultValue & ResultBase { + (): ResultValue; + (value: TValue): ResultValue; +} = function (val?: TValue): ResultValue { return { hasError (): boolean { return false; @@ -64,7 +64,7 @@ const value: { }; }; -type Result = ResultBase; +type Result = ResultValue | ResultError; export type { ResultValue, diff --git a/test/unit/ResultTests.ts b/test/unit/ResultTests.ts index 1a887e5..fc999ee 100644 --- a/test/unit/ResultTests.ts +++ b/test/unit/ResultTests.ts @@ -76,15 +76,6 @@ suite('Result', (): void => { assert.that(resultHasError).is.false(); }); - test(`narrows the type to the error case and discards value type information.`, async (): Promise => { - const result = getValue(); - - if (result.hasError()) { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const newResult: Result<{ something: 'elseEntirely' }, Error> = result; - } - }); - test('extrapolates value type correctly.', async (): Promise => { const result = getValue(); @@ -114,19 +105,6 @@ suite('Result', (): void => { assert.that(resultHasValue).is.false(); }); - test(`narrows the type to the value case and discards error type information.`, async (): Promise => { - const result = getValue(); - - interface CustomError extends Error { - bar: string; - } - - if (result.hasValue()) { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const newResult: Result = result; - } - }); - test('extrapolates error type correctly.', async (): Promise => { const result = getError(); @@ -268,39 +246,4 @@ suite('Result', (): void => { } }); }); - - test('is assignable to a result with the same error type if the result is known to be an error.', async (): Promise => { - class CustomError extends Error { - public someProp = 0; - } - - // This function compiling is enough for this test. - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const someFunction = function (someResult: Result): Result { - if (someResult.hasError()) { - return someResult; - } - - return error(new CustomError()); - }; - }); - - test('is assignable to a result with the same value type if the result is known to be an value.', async (): Promise => { - class CustomError1 extends Error { - public someProp = 0; - } - class CustomError2 extends Error { - public someOtherProp = 'some string'; - } - - // This function compiling is enough for this test. - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const someFunction = function (someResult: Result): Result { - if (someResult.hasValue()) { - return someResult; - } - - return value(''); - }; - }); });