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

Enforce no-implicit-any in every combinator #166

Merged
merged 1 commit into from
Jul 19, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 98 additions & 68 deletions src/combinators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ function mergeObjects<T extends unknown[] = unknown[]>(
* ```
*/
function pipe<
Fns extends [Internal.AnyFn, ...Internal.AnyFn[]],
Fns extends [Function, ...Function[]],
>(
...fns: Fns
): PipeReturn<CanComposeInSequence<Internal.Composables<Fns>>> {
const last = <T extends any[]>(arr: T): Last<T> => arr.at(-1)
return map(sequence(...fns), last as never) as PipeReturn<
return map(sequence(...fns as never), last as never) as PipeReturn<
CanComposeInSequence<Internal.Composables<Fns>>
>
}
Expand All @@ -90,7 +90,7 @@ function pipe<
* // ^? Composable<(id: number) => [string, number, boolean]>
* ```
*/
function all<Fns extends Internal.AnyFn[]>(
function all<Fns extends Function[]>(
...fns: Fns
): Composable<
(
Expand All @@ -102,7 +102,9 @@ function all<Fns extends Internal.AnyFn[]>(
}
> {
const callable = (async (...args) => {
const results = await Promise.all(fns.map((fn) => composable(fn)(...args)))
const results = await Promise.all(
fns.map((fn) => composable(fn as Internal.AnyFn)(...args)),
)

if (results.some(({ success }) => success === false)) {
return failure(results.map(({ errors }) => errors).flat())
Expand Down Expand Up @@ -136,25 +138,9 @@ function all<Fns extends Internal.AnyFn[]>(
* // ^? Composable<() => { a: string, b: number }>
* ```
*/
function collect<Fns extends Record<string, Internal.AnyFn>>(
function collect<Fns extends Record<string, Function>>(
fns: Fns,
): Composable<
(
...args: Parameters<
Exclude<
CanComposeInParallel<RecordToTuple<Internal.Composables<Fns>>>[0],
undefined
>
>
) => {
[key in keyof Fns]: UnpackData<Composable<Fns[key]>>
}
> {
const fnsWithKey = Object.entries(fns).map(([key, cf]) =>
map(cf, (result) => ({ [key]: result }))
)
const allFns = all(...(fnsWithKey as any)) as Composable
return map(allFns, mergeObjects) as Composable<
): Fns extends Record<string, Internal.AnyFn> ? Composable<
(
...args: Parameters<
Exclude<
Expand All @@ -166,6 +152,25 @@ function collect<Fns extends Record<string, Internal.AnyFn>>(
[key in keyof Fns]: UnpackData<Composable<Fns[key]>>
}
>
: never {
const fnsWithKey = Object.entries(fns).map(([key, cf]) =>
map(cf as Internal.AnyFn, (result) => ({ [key]: result }))
)
const allFns = all(...(fnsWithKey as any)) as Composable
return map(allFns, mergeObjects) as Fns extends Record<string, Internal.AnyFn>
? Composable<
(
...args: Parameters<
Exclude<
CanComposeInParallel<RecordToTuple<Internal.Composables<Fns>>>[0],
undefined
>
>
) => {
[key in keyof Fns]: UnpackData<Composable<Fns[key]>>
}
>
: never
}

/**
Expand All @@ -184,12 +189,15 @@ function collect<Fns extends Record<string, Internal.AnyFn>>(
*/

function sequence<
Fns extends [Internal.AnyFn, ...Internal.AnyFn[]],
Fns extends [Function, ...Function[]],
>(
...fns: Fns
): SequenceReturn<CanComposeInSequence<Internal.Composables<Fns>>> {
const callable = (async (...args) => {
const [head, ...tail] = fns
const [head, ...tail] = fns as unknown as [
Internal.AnyFn,
...Internal.AnyFn[],
]

const res = await composable(head)(...args)
if (!res.success) return failure(res.errors)
Expand Down Expand Up @@ -222,19 +230,21 @@ function sequence<
* // result === '1 -> 2'
* ```
*/
function map<Fn extends Internal.AnyFn, O>(
function map<Fn extends Function, O>(
fn: Fn,
mapper: (
res: UnpackData<Composable<Fn>>,
...originalInput: Parameters<Fn>
res: UnpackData<Composable<Extract<Fn, Internal.AnyFn>>>,
...originalInput: Parameters<Extract<Fn, Internal.AnyFn>>
) => O | Promise<O>,
): Composable<(...args: Parameters<Fn>) => O> {
): Fn extends Internal.AnyFn ? Composable<(...args: Parameters<Fn>) => O>
: never {
const callable = (async (...args) => {
const result = await composable(fn)(...args)
if (!result.success) return failure(result.errors)

return composable(mapper)(result.data, ...args)
}) as Composable<(...args: Parameters<Fn>) => O>
return composable(mapper)(result.data, ...(args as never))
}) as Fn extends Internal.AnyFn ? Composable<(...args: Parameters<Fn>) => O>
: never
callable.kind = 'composable' as const
return callable
}
Expand All @@ -254,18 +264,24 @@ function map<Fn extends Internal.AnyFn, O>(
* ```
*/
function mapParameters<
Fn extends Internal.AnyFn,
Fn extends Function,
NewParameters extends unknown[],
const MapperOutput extends Parameters<Composable<Fn>>,
const MapperOutput extends Parameters<
Composable<Extract<Fn, Internal.AnyFn>>
>,
>(
fn: Fn,
mapper: (...args: NewParameters) => Promise<MapperOutput> | MapperOutput,
): MapParametersReturn<Composable<Fn>, NewParameters, MapperOutput> {
): Fn extends Internal.AnyFn
? MapParametersReturn<Composable<Fn>, NewParameters, MapperOutput>
: never {
const callable = (async (...args) => {
const output = await composable(mapper)(...args)
if (!output.success) return failure(output.errors)
return composable(fn)(...output.data)
}) as MapParametersReturn<Composable<Fn>, NewParameters, MapperOutput>
}) as Fn extends Internal.AnyFn
? MapParametersReturn<Composable<Fn>, NewParameters, MapperOutput>
: never
callable.kind = 'composable' as const
return callable
}
Expand All @@ -285,31 +301,38 @@ function mapParameters<
* ```
*/
function catchFailure<
Fn extends Internal.AnyFn,
C extends (err: Error[], ...originalInput: Parameters<Fn>) => any,
Fn extends Function,
C extends (
err: Error[],
...originalInput: Parameters<Extract<Fn, Internal.AnyFn>>
) => any,
>(
fn: Fn,
catcher: C,
): Composable<
(
...args: Parameters<Fn>
) => Awaited<ReturnType<C>> extends never[]
? UnpackData<Composable<Fn>> extends any[] ? UnpackData<Composable<Fn>>
: Awaited<ReturnType<C>> | UnpackData<Composable<Fn>>
: Awaited<ReturnType<C>> | UnpackData<Composable<Fn>>
> {
const callable = (async (...args: Parameters<Fn>) => {
const res = await composable(fn)(...args)
if (res.success) return success(res.data)
return composable(catcher)(res.errors, ...(args as never))
}) as Composable<
): Fn extends Internal.AnyFn ? Composable<
(
...args: Parameters<Fn>
) => Awaited<ReturnType<C>> extends never[]
? UnpackData<Composable<Fn>> extends any[] ? UnpackData<Composable<Fn>>
: Awaited<ReturnType<C>> | UnpackData<Composable<Fn>>
: Awaited<ReturnType<C>> | UnpackData<Composable<Fn>>
>
: never {
const callable =
(async (...args: Parameters<Extract<Fn, Internal.AnyFn>>) => {
const res = await composable(fn)(...args)
if (res.success) return success(res.data)
return composable(catcher)(res.errors, ...(args as never))
}) as Fn extends Internal.AnyFn ? Composable<
(
...args: Parameters<Fn>
) => Awaited<ReturnType<C>> extends never[]
? UnpackData<Composable<Fn>> extends any[]
? UnpackData<Composable<Fn>>
: Awaited<ReturnType<C>> | UnpackData<Composable<Fn>>
: Awaited<ReturnType<C>> | UnpackData<Composable<Fn>>
>
: never
callable.kind = 'composable' as const
return callable
}
Expand All @@ -328,10 +351,10 @@ function catchFailure<
* }))
* ```
*/
function mapErrors<Fn extends Internal.AnyFn>(
function mapErrors<Fn extends Function>(
fn: Fn,
mapper: (err: Error[]) => Error[] | Promise<Error[]>,
): Composable<Fn> {
): Fn extends Internal.AnyFn ? Composable<Fn> : never {
const callable = (async (...args) => {
const res = await composable(fn)(...args)
if (res.success) return success(res.data)
Expand All @@ -341,7 +364,7 @@ function mapErrors<Fn extends Internal.AnyFn>(
} else {
return failure(mapped.errors)
}
}) as Composable<Fn>
}) as Fn extends Internal.AnyFn ? Composable<Fn> : never
callable.kind = 'composable' as const
return callable
}
Expand Down Expand Up @@ -369,9 +392,9 @@ function trace(
result: Result<unknown>,
...originalInput: unknown[]
) => Promise<void> | void,
): <Fn extends Internal.AnyFn>(
): <Fn extends Function>(
fn: Fn,
) => Composable<Fn> {
) => Fn extends Internal.AnyFn ? Composable<Fn> : never {
return ((fn) => {
const callable = async (...args: any) => {
const originalResult = await composable(fn)(...args)
Expand All @@ -382,9 +405,9 @@ function trace(
}
callable.kind = 'composable' as const
return callable
}) as <Fn extends Internal.AnyFn>(
}) as <Fn extends Function>(
fn: Fn,
) => Composable<Fn>
) => Fn extends Internal.AnyFn ? Composable<Fn> : never
}

/**
Expand All @@ -409,24 +432,31 @@ function trace(
* ```
*/
function branch<
SourceComposable extends Internal.AnyFn,
SourceComposable extends Function,
Resolver extends (
o: UnpackData<Composable<SourceComposable>>,
o: UnpackData<Composable<Extract<SourceComposable, Internal.AnyFn>>>,
) => Internal.AnyFn | null | Promise<Internal.AnyFn | null>,
>(
cf: SourceComposable,
resolver: Resolver,
): BranchReturn<Composable<SourceComposable>, Resolver> {
const callable = (async (...args: Parameters<SourceComposable>) => {
const result = await composable(cf)(...args)
if (!result.success) return result
): BranchReturn<
Composable<Extract<SourceComposable, Internal.AnyFn>>,
Resolver
> {
const callable =
(async (...args: Parameters<Extract<SourceComposable, Internal.AnyFn>>) => {
const result = await composable(cf)(...args)
if (!result.success) return result

return composable(async () => {
const nextComposable = await resolver(result.data)
if (typeof nextComposable !== 'function') return result.data
return fromSuccess(composable(nextComposable))(result.data)
})()
}) as BranchReturn<Composable<SourceComposable>, Resolver>
return composable(async () => {
const nextComposable = await resolver(result.data)
if (typeof nextComposable !== 'function') return result.data
return fromSuccess(composable(nextComposable))(result.data)
})()
}) as BranchReturn<
Composable<Extract<SourceComposable, Internal.AnyFn>>,
Resolver
>
;(callable as any).kind = 'composable' as const
return callable
}
Expand Down
53 changes: 31 additions & 22 deletions src/context/combinators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,18 @@ function applyContextToList<
* // ^? ComposableWithSchema<boolean>
* ```
*/
function pipe<Fns extends Internal.AnyFn[]>(
function pipe<Fns extends Function[]>(
...fns: Fns
): PipeReturn<Internal.Composables<Fns>> {
const callable =
((input: any, context: any) =>
A.pipe(...applyContextToList(fns, context) as [
const callable = ((input: any, context: any) =>
A.pipe(
...applyContextToList(fns as unknown as Internal.AnyFn[], context) as [
Composable,
...Composable[],
])(input)) as PipeReturn<
Internal.Composables<Fns>
>
],
)(input)) as PipeReturn<
Internal.Composables<Fns>
>
;(callable as any).kind = 'composable' as const
return callable
}
Expand All @@ -58,12 +59,15 @@ function pipe<Fns extends Internal.AnyFn[]>(
* ```
*/

function sequence<Fns extends Internal.AnyFn[]>(
function sequence<Fns extends Function[]>(
...fns: Fns
): SequenceReturn<Internal.Composables<Fns>> {
const callable = ((input: any, context: any) =>
A.sequence(
...applyContextToList(fns, context) as [Composable, ...Composable[]],
...applyContextToList(fns as unknown as Internal.AnyFn[], context) as [
Composable,
...Composable[],
],
)(
input,
)) as SequenceReturn<Internal.Composables<Fns>>
Expand All @@ -75,25 +79,30 @@ function sequence<Fns extends Internal.AnyFn[]>(
* Like branch but preserving the context parameter.
*/
function branch<
SourceComposable extends Internal.AnyFn,
SourceComposable extends Function,
Resolver extends (
o: UnpackData<Composable<SourceComposable>>,
o: UnpackData<Composable<Extract<SourceComposable, Internal.AnyFn>>>,
) => Internal.AnyFn | null | Promise<Internal.AnyFn | null>,
>(
cf: SourceComposable,
resolver: Resolver,
): BranchReturn<Composable<SourceComposable>, Resolver> {
const callable = (async (...args: Parameters<SourceComposable>) => {
const [input, context] = args
const result = await composable(cf)(input, context)
if (!result.success) return result
): SourceComposable extends Internal.AnyFn
? BranchReturn<Composable<SourceComposable>, Resolver>
: never {
const callable =
(async (...args: Parameters<Extract<SourceComposable, Internal.AnyFn>>) => {
const [input, context] = args
const result = await composable(cf)(input, context)
if (!result.success) return result

return composable(async () => {
const nextFn = await resolver(result.data)
if (typeof nextFn !== 'function') return result.data
return fromSuccess(composable(nextFn))(result.data, context)
})()
}) as BranchReturn<Composable<SourceComposable>, Resolver>
return composable(async () => {
const nextFn = await resolver(result.data)
if (typeof nextFn !== 'function') return result.data
return fromSuccess(composable(nextFn))(result.data, context)
})()
}) as SourceComposable extends Internal.AnyFn
? BranchReturn<Composable<SourceComposable>, Resolver>
: never
;(callable as any).kind = 'composable' as const
return callable
}
Expand Down
Loading
Loading