Skip to content
New issue

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

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

Already on GitHub? # to your account

Support Refinement in Preficate.tuple and Predicate.struct #3366

Merged
merged 2 commits into from
Jul 29, 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
5 changes: 5 additions & 0 deletions .changeset/purple-onions-drive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": minor
---

Support `Refinement` in `Predicate.tuple` and `Predicate.struct`
48 changes: 48 additions & 0 deletions packages/effect/dtslint/Predicate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,51 @@ pipe(Predicate.isString, Predicate.or(Predicate.isNumber))

// $ExpectType Refinement<unknown, string | number>
Predicate.or(Predicate.isString, Predicate.isNumber)

// -------------------------------------------------------------------------------------
// tuple
// -------------------------------------------------------------------------------------

const isA = hole<Predicate.Refinement<string, "a">>()
const isTrue = hole<Predicate.Refinement<boolean, true>>()
const isOdd = hole<Predicate.Predicate<number>>()

// $ExpectType Refinement<readonly [boolean, string], readonly [true, "a"]>
Predicate.tuple(isTrue, isA)

// $ExpectType Refinement<readonly [boolean, number], readonly [true, number]>
Predicate.tuple(isTrue, isOdd)

// $ExpectType Predicate<readonly [number, number]>
Predicate.tuple(isOdd, isOdd)

// $ExpectType Predicate<readonly number[]>
Predicate.tuple(...hole<Array<Predicate.Predicate<number>>>())

// $ExpectType Refinement<readonly never[], readonly never[]>
Predicate.tuple(...hole<Array<Predicate.Predicate<number> | Predicate.Refinement<boolean, true>>>())

// $ExpectType Refinement<readonly boolean[], readonly true[]>
Predicate.tuple(...hole<Array<Predicate.Refinement<boolean, true>>>())

// -------------------------------------------------------------------------------------
// struct
// -------------------------------------------------------------------------------------

// $ExpectType Refinement<{ readonly a: string; readonly true: boolean; }, { readonly a: "a"; readonly true: true; }>
Predicate.struct({
a: isA,
true: isTrue
})

// $ExpectType Refinement<{ readonly odd: number; readonly true: boolean; }, { readonly odd: number; readonly true: true; }>
Predicate.struct({
odd: isOdd,
true: isTrue
})

// $ExpectType Predicate<{ readonly odd: number; readonly odd1: number; }>
Predicate.struct({
odd: isOdd,
odd1: isOdd
})
76 changes: 68 additions & 8 deletions packages/effect/src/Predicate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,45 @@ export interface Refinement<in A, out B extends A> {
(a: A): a is B
}

/**
* @since 3.6.0
* @category type-level
*/
export declare namespace Predicate {
/**
* @since 3.6.0
* @category type-level
*/
export type In<T extends Any> = [T] extends [Predicate<infer _A>] ? _A : never
/**
* @since 3.6.0
* @category type-level
*/
export type Any = Predicate<any>
}

/**
* @since 3.6.0
* @category type-level
*/
export declare namespace Refinement {
/**
* @since 3.6.0
* @category type-level
*/
export type In<T extends Any> = [T] extends [Refinement<infer _A, infer _>] ? _A : never
/**
* @since 3.6.0
* @category type-level
*/
export type Out<T extends Any> = [T] extends [Refinement<infer _, infer _B>] ? _B : never
/**
* @since 3.6.0
* @category type-level
*/
export type Any = Refinement<any, any>
}

/**
* Given a `Predicate<A>` returns a `Predicate<B>`
*
Expand Down Expand Up @@ -686,31 +725,52 @@ export const productMany = <A>(
* Similar to `Promise.all` but operates on `Predicate`s.
*
* ```
* [Refinement<A, B>, Refinement<C, D>, ...] -> Refinement<[A, C, ...], [B, D, ...]>
* [Predicate<A>, Predicate<B>, ...] -> Predicate<[A, B, ...]>
* [Refinement<A, B>, Predicate<C>, ...] -> Refinement<[A, C, ...], [B, C, ...]>
* ```
*
* @since 2.0.0
*/
export const tuple = <T extends ReadonlyArray<Predicate<any>>>(
...elements: T
): Predicate<Readonly<{ [I in keyof T]: [T[I]] extends [Predicate<infer A>] ? A : never }>> => all(elements) as any
export const tuple: {
<T extends ReadonlyArray<Predicate.Any>>(
...elements: T
): [Extract<T[number], Refinement.Any>] extends [never] ? Predicate<{ readonly [I in keyof T]: Predicate.In<T[I]> }>
: Refinement<
{ readonly [I in keyof T]: T[I] extends Refinement.Any ? Refinement.In<T[I]> : Predicate.In<T[I]> },
{ readonly [I in keyof T]: T[I] extends Refinement.Any ? Refinement.Out<T[I]> : Predicate.In<T[I]> }
>
} = (...elements: ReadonlyArray<Predicate.Any>) => all(elements) as any

/**
* ```
* { ab: Refinement<A, B>; cd: Refinement<C, D>, ... } -> Refinement<{ ab: A; cd: C; ... }, { ab: B; cd: D; ... }>
* { a: Predicate<A, B>; b: Predicate<B>, ... } -> Predicate<{ a: A; b: B; ... }>
* { ab: Refinement<A, B>; c: Predicate<C>, ... } -> Refinement<{ ab: A; c: C; ... }, { ab: B; c: С; ... }>
* ```
*
* @since 2.0.0
*/
export const struct = <R extends Record<string, Predicate<any>>>(
fields: R
): Predicate<{ readonly [K in keyof R]: [R[K]] extends [Predicate<infer A>] ? A : never }> => {
export const struct: {
<R extends Record<string, Predicate.Any>>(
fields: R
): [Extract<R[keyof R], Refinement.Any>] extends [never] ?
Predicate<{ readonly [K in keyof R]: Predicate.In<R[K]> }> :
Refinement<
{ readonly [K in keyof R]: R[K] extends Refinement.Any ? Refinement.In<R[K]> : Predicate.In<R[K]> },
{ readonly [K in keyof R]: R[K] extends Refinement.Any ? Refinement.Out<R[K]> : Predicate.In<R[K]> }
>
} = (<R extends Record<string, Predicate.Any>>(fields: R) => {
const keys = Object.keys(fields)
return (a) => {
return (a: Record<string, unknown>) => {
for (const key of keys) {
if (!fields[key](a[key])) {
return false
}
}
return true
}
}
}) as any

/**
* Negates the result of a given predicate.
Expand Down