Skip to content

Commit

Permalink
Merge branch 'master' into schniz/remove-maintainer-callout
Browse files Browse the repository at this point in the history
  • Loading branch information
supermacro authored Aug 5, 2024
2 parents 036b8ad + ced9108 commit 8aac6ae
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/mean-eagles-develop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"neverthrow": patch
---

fix: change type definitions to make inferring types of safeTry more strict
21 changes: 21 additions & 0 deletions src/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ export const err = <T = never, E = unknown>(err: E): Err<T, E> => new Err(err)
* @returns The first occurence of either an yielded Err or a returned Result.
*/
export function safeTry<T, E>(body: () => Generator<Err<never, E>, Result<T, E>>): Result<T, E>
export function safeTry<
YieldErr extends Err<never, unknown>,
GeneratorReturnResult extends Result<unknown, unknown>
>(
body: () => Generator<YieldErr, GeneratorReturnResult>,
): Result<
InferOkTypes<GeneratorReturnResult>,
InferErrTypes<YieldErr> | InferErrTypes<GeneratorReturnResult>
>

/**
* Evaluates the given generator to a Result returned or an Err yielded from it,
* whichever comes first.
Expand All @@ -95,6 +105,17 @@ export function safeTry<T, E>(body: () => Generator<Err<never, E>, Result<T, E>>
export function safeTry<T, E>(
body: () => AsyncGenerator<Err<never, E>, Result<T, E>>,
): Promise<Result<T, E>>
export function safeTry<
YieldErr extends Err<never, unknown>,
GeneratorReturnResult extends Result<unknown, unknown>
>(
body: () => AsyncGenerator<YieldErr, GeneratorReturnResult>,
): Promise<
Result<
InferOkTypes<GeneratorReturnResult>,
InferErrTypes<YieldErr> | InferErrTypes<GeneratorReturnResult>
>
>
export function safeTry<T, E>(
body:
| (() => Generator<Err<never, E>, Result<T, E>>)
Expand Down
154 changes: 153 additions & 1 deletion tests/typecheck-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
Result,
ResultAsync,
} from '../src'
import { Transpose } from '../src/result'
import { safeTry, Transpose } from '../src/result'
import { type N, Test } from 'ts-toolbelt'

type CreateTuple<L, V = string> =
Expand Down Expand Up @@ -1961,6 +1961,158 @@ type CreateTuple<L, V = string> =
});

(function describe(_ = 'Utility types') {
(function describe(_ = 'safeTry') {
(function describe(_ = 'sync generator') {
(function it(_ = 'should correctly infer the result type when generator returns Ok') {
interface ReturnMyError {
name: 'ReturnMyError'
}

type Expectation = Result<string, ReturnMyError>

const result = safeTry(function *() {
return ok<string, ReturnMyError>('string');
})
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});

(function it(_ = 'should correctly infer the result type when generator returns Err') {
interface ReturnMyError {
name: 'ReturnMyError';
}

type Expectation = Result<string, ReturnMyError>

const result = safeTry(function *() {
return err<string, ReturnMyError>({ name: 'ReturnMyError' });
})
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});

(function it(_ = 'infers the value type when calling "yield*"') {
interface YieldMyError {
name: 'YieldMyError';
}
interface ReturnMyError {
name: 'ReturnMyError';
}

safeTry(function *() {
type Expectation = number

const unwrapped = yield* ok<number, YieldMyError>(123).safeUnwrap();
Test.checks([
Test.check<typeof unwrapped, Expectation, Test.Pass>(),
])

return ok<string, ReturnMyError>('string');
})
});

(function it(_ = 'should correctly infer the result type with multiple "yield*"') {
interface FirstYieldMyError {
name: 'FirstYieldMyError';
}
interface SecondYieldMyError {
name: 'SecondYieldMyError';
}
interface ReturnMyError {
name: 'ReturnMyError';
}

type Expectation = Result<string, FirstYieldMyError | SecondYieldMyError | ReturnMyError>

const result = safeTry(function *() {
yield* ok<number, FirstYieldMyError>(123).safeUnwrap();
yield* err<never, SecondYieldMyError>({ name: 'SecondYieldMyError' }).safeUnwrap();
return ok<string, ReturnMyError>('string');
})
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
});

(function describe(_ = 'async generator') {
(function it(_ = 'should correctly infer the result type when generator returns OkAsync') {
interface ReturnMyError {
name: 'ReturnMyError'
}

type Expectation = Promise<Result<string, ReturnMyError>>

const result = safeTry(async function *() {
return okAsync<string, ReturnMyError>('string');
})
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});

(function it(_ = 'should correctly infer the result type when generator returns ErrAsync') {
interface ReturnMyError {
name: 'ReturnMyError';
}

type Expectation = Promise<Result<string, ReturnMyError>>

const result = safeTry(async function *() {
return errAsync<string, ReturnMyError>({ name: 'ReturnMyError' });
})
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});

(function it(_ = 'infers the value type when calling "yield*"') {
interface YieldMyError {
name: 'YieldMyError';
}
interface ReturnMyError {
name: 'ReturnMyError';
}

safeTry(async function *() {
type Expectation = number

const unwrapped = yield* okAsync<number, YieldMyError>(123).safeUnwrap();
Test.checks([
Test.check<typeof unwrapped, Expectation, Test.Pass>(),
])

return ok<string, ReturnMyError>('string');
})
});

(function it(_ = 'should correctly infer the result type with multiple "yield*"') {
interface FirstYieldMyError {
name: 'FirstYieldMyError';
}
interface SecondYieldMyError {
name: 'SecondYieldMyError';
}
interface ReturnMyError {
name: 'ReturnMyError';
}

type Expectation = Promise<Result<string, FirstYieldMyError | SecondYieldMyError | ReturnMyError>>

const result = safeTry(async function *() {
yield* okAsync<number, FirstYieldMyError>(123).safeUnwrap();
yield* errAsync<never, SecondYieldMyError>({ name: 'SecondYieldMyError' }).safeUnwrap();
return okAsync<string, ReturnMyError>('string');
})
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
});
});

(function describe(_ = 'Transpose') {
(function it(_ = 'should transpose an array') {
const input: [
Expand Down

0 comments on commit 8aac6ae

Please sign in to comment.