Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix types for Result.combine and ResultAsync.combine for arrays witho… #435

Merged
merged 1 commit into from Dec 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/result-async.ts
Expand Up @@ -11,6 +11,8 @@ import { Err, Ok, Result } from './'
import {
combineResultAsyncList,
combineResultAsyncListWithAllErrors,
ExtractErrAsyncTypes,
ExtractOkAsyncTypes,
InferAsyncErrTypes,
InferAsyncOkTypes,
InferErrTypes,
Expand Down Expand Up @@ -154,10 +156,18 @@ export const fromPromise = ResultAsync.fromPromise
export const fromSafePromise = ResultAsync.fromSafePromise

// Combines the array of async results into one result.
export type CombineResultAsyncs<T> = TraverseAsync<UnwrapAsync<T>>
export type CombineResultAsyncs<
T extends readonly ResultAsync<unknown, unknown>[]
> = IsLiteralArray<T> extends 1
? TraverseAsync<UnwrapAsync<T>>
: ResultAsync<ExtractOkAsyncTypes<T>, ExtractErrAsyncTypes<T>[number]>

// Combines the array of async results into one result with all errors.
export type CombineResultsWithAllErrorsArrayAsync<T> = TraverseWithAllErrorsAsync<UnwrapAsync<T>>
export type CombineResultsWithAllErrorsArrayAsync<
T extends readonly ResultAsync<unknown, unknown>[]
> = IsLiteralArray<T> extends 1
? TraverseWithAllErrorsAsync<UnwrapAsync<T>>
: ResultAsync<ExtractOkAsyncTypes<T>, ExtractErrAsyncTypes<T>[number][]>

// Unwraps the inner `Result` from a `ResultAsync` for all elements.
type UnwrapAsync<T> = IsLiteralArray<T> extends 1
Expand Down
26 changes: 12 additions & 14 deletions src/result.ts
Expand Up @@ -3,6 +3,8 @@ import { createNeverThrowError, ErrorConfig } from './_internals/error'
import {
combineResultList,
combineResultListWithAllErrors,
ExtractErrTypes,
ExtractOkTypes,
InferErrTypes,
InferOkTypes,
} from './_internals/utils'
Expand Down Expand Up @@ -402,6 +404,8 @@ export type Combine<T, Depth extends number = 5> = Transpose<CollectResults<T>,
infer R,
]
? [UnknownMembersToNever<L>, UnknownMembersToNever<R>]
: Transpose<CollectResults<T>, [], Depth> extends []
? [[], []]
: never

// Deduplicates the result, as the result type is a union of Err and Ok types.
Expand Down Expand Up @@ -472,23 +476,17 @@ type TraverseWithAllErrors<T, Depth extends number = 5> = Combine<T, Depth> exte
: never

// Combines the array of results into one result.
export type CombineResults<T> = T extends ReadonlyArray<infer U>
? IsLiteralArray<T> extends 1
? Traverse<T>
: Traverse<MemberListOf<Dedup<U>>> extends Result<infer L, infer R>
? Result<L, EmptyArrayToNever<R>>
: never
: never
export type CombineResults<
T extends readonly Result<unknown, unknown>[]
> = IsLiteralArray<T> extends 1
? Traverse<T>
: Result<ExtractOkTypes<T>, ExtractErrTypes<T>[number]>

// Combines the array of results into one result with all errors.
export type CombineResultsWithAllErrorsArray<
T extends readonly Result<unknown, unknown>[]
> = T extends ReadonlyArray<infer U>
? IsLiteralArray<T> extends 1
? TraverseWithAllErrors<T>
: TraverseWithAllErrors<MemberListOf<Dedup<U>>> extends Result<infer L, infer R>
? Result<EmptyArrayToNever<L>, EmptyArrayToNever<R>>
: never
: never
> = IsLiteralArray<T> extends 1
? TraverseWithAllErrors<T>
: Result<ExtractOkTypes<T>, ExtractErrTypes<T>[number][]>

//#endregion
234 changes: 191 additions & 43 deletions tests/typecheck-tests.ts
Expand Up @@ -243,6 +243,116 @@ import { Transpose } from '../src/result'
.asyncAndThen((val) => errAsync<string, string[]>(['oh nooooo']))
});
});

(function describe(_ = 'combine') {
(function it(_ = 'combines different results into one') {
type Expectation = Result<[ number, string, boolean, boolean ], Error | string | string[]>;

const result = Result.combine([
ok<number, string>(1),
ok<string, string>('string'),
err<boolean, string[]>([ 'string', 'string2' ]),
err<boolean, Error>(new Error('error content')),
])

const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});

(function it(_ = 'combines only ok results into one') {
type Expectation = Result<[ number, string ], never>;

const result = Result.combine([
ok(1),
ok('string'),
]);

const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});

(function it(_ = 'combines only err results into one') {
type Expectation = Result<[ never, never ], number | string>;

const result = Result.combine([
err(1),
err('string'),
]);

const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});

(function it(_ = 'combines empty list results into one') {
type Expectation = Result<never, never>;
const results: [] = [];

const result = Result.combine(results);

const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});

(function it(_ = 'combines arrays of results to a result of an array') {
type Expectation = Result<string[], string>;
const results: Result<string, string>[] = [];

const result = Result.combine(results);

const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});
});

(function describe(_ = 'combineWithAllErrors') {
(function it(_ = 'combines different results into one') {
type Expectation = Result<[ number, string, never, never ], [never, never, string[], Error]>;

const result = Result.combineWithAllErrors([
ok(1),
ok('string'),
err([ 'string', 'string2' ]),
err(new Error('error content')),
]);

const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});

(function it(_ = 'combines only ok results into one') {
type Expectation = Result<[ number, string ], [never, never]>;

const result = Result.combineWithAllErrors([
ok(1),
ok('string'),
]);

const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});

(function it(_ = 'combines only err results into one') {
type Expectation = Result<[ never, never ], [number, string]>;

const result = Result.combineWithAllErrors([
err(1),
err('string'),
]);

const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});

(function it(_ = 'combines arrays of results to a result of an array') {
type Expectation = Result<string[], (number | string)[]>;
const results: Result<string, number | string>[] = [];

const result = Result.combineWithAllErrors(results);

const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});
});
});


Expand Down Expand Up @@ -557,79 +667,117 @@ import { Transpose } from '../src/result'
})
});
});
});


(function describe(_ = 'Combine on Unbounded lists') {
(function describe(_ = 'combine') {
(function it(_ = 'combines different results into one') {
type Expectation = Result<[ number, string, boolean, boolean ], Error | string | string[]>;

const result: Expectation = Result.combine([
ok<number, string>(1),
ok<string, string>('string'),
err<boolean, string[]>([ 'string', 'string2' ]),
err<boolean, Error>(new Error('error content')),
(function it(_ = 'combines different result asyncs into one') {
type Expectation = ResultAsync<[ number, string, boolean, boolean ], Error | string | string[]>;

const result = ResultAsync.combine([
okAsync<number, string>(1),
okAsync<string, string>('string'),
errAsync<boolean, string[]>([ 'string', 'string2' ]),
errAsync<boolean, Error>(new Error('error content')),
])

const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});

(function it(_ = 'combines only ok results into one') {
type Expectation = Result<[ number, string ], never>;
(function it(_ = 'combines only ok result asyncs into one') {
type Expectation = ResultAsync<[ number, string ], never>;

const result: Expectation = Result.combine([
ok(1),
ok('string'),
const result = ResultAsync.combine([
okAsync(1),
okAsync('string'),
]);

const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});

(function it(_ = 'combines only err results into one') {
type Expectation = Result<[ never, never ], number | string>;
type Expectation = ResultAsync<[ never, never ], number | string>;

const result: Expectation = Result.combine([
err(1),
err('string'),
const result = ResultAsync.combine([
errAsync(1),
errAsync('string'),
]);

const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});

(function it(_ = 'combines empty list results into one') {
type Expectation = Result<never[], never>;
(function it(_ = 'combines empty list result asyncs into one') {
type Expectation = ResultAsync<never, never>;
const results: [] = [];

const result = ResultAsync.combine(results);

const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});

const result: Expectation = Result.combine([]);
(function it(_ = 'combines arrays of result asyncs to a result async of an array') {
type Expectation = ResultAsync<string[], string>;
const results: ResultAsync<string, string>[] = [];

const result = ResultAsync.combine(results);

const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});
});

(function describe(_ = 'combineWithAllErrors') {
(function it(_ = 'combines different results into one') {
type Expectation = Result<[ number, string, never, never ], [never, never, string[], Error]>;

const result: Expectation = Result.combineWithAllErrors([
ok(1),
ok('string'),
err([ 'string', 'string2' ]),
err(new Error('error content')),
(function it(_ = 'combines different result asyncs into one') {
type Expectation = ResultAsync<[ number, string, never, never ], [never, never, string[], Error]>;

const result = ResultAsync.combineWithAllErrors([
okAsync(1),
okAsync('string'),
errAsync([ 'string', 'string2' ]),
errAsync(new Error('error content')),
]);

const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});

(function it(_ = 'combines only ok results into one') {
type Expectation = Result<[ number, string ], never[]>;
(function it(_ = 'combines only ok result asyncs into one') {
type Expectation = ResultAsync<[ number, string ], [never, never]>;

const result: Expectation = Result.combineWithAllErrors([
ok(1),
ok('string'),
const result = ResultAsync.combineWithAllErrors([
okAsync(1),
okAsync('string'),
]);

const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});

(function it(_ = 'combines only err results into one') {
type Expectation = Result<[ never, never ], [number, string]>;
(function it(_ = 'combines only err result asyncs into one') {
type Expectation = ResultAsync<[ never, never ], [number, string]>;

const result: Expectation = Result.combineWithAllErrors([
err(1),
err('string'),
const result = ResultAsync.combineWithAllErrors([
errAsync(1),
errAsync('string'),
]);

const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});
});
})();

(function it(_ = 'combines arrays of result asyncs to a result of an array') {
type Expectation = ResultAsync<string[], (number | string)[]>;
const results: ResultAsync<string, number | string>[] = [];

const result = ResultAsync.combineWithAllErrors(results);

const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});
});
});

(function describe(_ = 'Utility types') {
(function describe(_ = 'Transpose') {
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Expand Up @@ -7,6 +7,7 @@
"noUnusedLocals": true,
"noUnusedParameters": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"declaration": true,
"moduleResolution": "Node",
"baseUrl": "./src",
Expand Down