Skip to content

Commit

Permalink
feat: update IsVoid IsNotVoid
Browse files Browse the repository at this point in the history
  • Loading branch information
unional committed Sep 25, 2023
1 parent 3503ed9 commit c1c5882
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 76 deletions.
85 changes: 54 additions & 31 deletions type-plus/ts/void/is_not_void.spec.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,70 @@
import { it } from '@jest/globals'
import { testType, type $Else, type $SelectionPredicate, type $Then, type IsNotVoid } from '../index.js'
import { testType, type $Else, type $Then, type IsNotVoid } from '../index.js'

it('returns false for void', () => {
testType.equal<IsNotVoid<void>, $Else>(true)
testType.equal<IsNotVoid<void>, false>(true)
})

it('returns true for other special types', () => {
testType.equal<IsNotVoid<any>, $Then>(true)
testType.equal<IsNotVoid<unknown>, $Then>(true)
testType.equal<IsNotVoid<never>, $Then>(true)
testType.equal<IsNotVoid<any>, true>(true)
testType.equal<IsNotVoid<unknown>, true>(true)
testType.equal<IsNotVoid<never>, true>(true)
})

it('returns true for other types', () => {
testType.equal<IsNotVoid<undefined>, $Then>(true)
testType.equal<IsNotVoid<null>, $Then>(true)
testType.equal<IsNotVoid<number>, $Then>(true)
testType.equal<IsNotVoid<boolean>, $Then>(true)
testType.equal<IsNotVoid<true>, $Then>(true)
testType.equal<IsNotVoid<false>, $Then>(true)
testType.equal<IsNotVoid<string>, $Then>(true)
testType.equal<IsNotVoid<''>, $Then>(true)
testType.equal<IsNotVoid<symbol>, $Then>(true)
testType.equal<IsNotVoid<bigint>, $Then>(true)
testType.equal<IsNotVoid<{}>, $Then>(true)
testType.equal<IsNotVoid<string[]>, $Then>(true)
testType.equal<IsNotVoid<[]>, $Then>(true)
testType.equal<IsNotVoid<Function>, $Then>(true)
testType.equal<IsNotVoid<() => void>, $Then>(true)
})

it('returns true for union type', () => {
testType.equal<IsNotVoid<void | 1>, $Then>(true)
testType.equal<IsNotVoid<undefined>, true>(true)
testType.equal<IsNotVoid<null>, true>(true)
testType.equal<IsNotVoid<number>, true>(true)
testType.equal<IsNotVoid<boolean>, true>(true)
testType.equal<IsNotVoid<true>, true>(true)
testType.equal<IsNotVoid<false>, true>(true)
testType.equal<IsNotVoid<string>, true>(true)
testType.equal<IsNotVoid<''>, true>(true)
testType.equal<IsNotVoid<symbol>, true>(true)
testType.equal<IsNotVoid<bigint>, true>(true)
testType.equal<IsNotVoid<{}>, true>(true)
testType.equal<IsNotVoid<string[]>, true>(true)
testType.equal<IsNotVoid<[]>, true>(true)
testType.equal<IsNotVoid<Function>, true>(true)
testType.equal<IsNotVoid<() => void>, true>(true)
})

it('distributes for union type', () => {
testType.equal<IsNotVoid<void | 1>, boolean>(true)
})

it('returns false for intersection type', () => {
testType.equal<IsNotVoid<void & { a: 1 }>, $Else>(true)
testType.equal<IsNotVoid<void & { a: 1 }>, false>(true)
})

it('works as filter', () => {
testType.equal<IsNotVoid<void, { selection: 'filter' }>, never>(true)

testType.equal<IsNotVoid<never, { selection: 'filter' }>, never>(true)
testType.equal<IsNotVoid<unknown, { selection: 'filter' }>, unknown>(true)
testType.equal<IsNotVoid<string | boolean, { selection: 'filter' }>, string|boolean>(true)

testType.equal<string | never, string>(true)
testType.equal<IsNotVoid<string | void, { selection: 'filter' }>, string>(true)

testType.equal<IsNotVoid<string | boolean, { selection: 'filter-unknown' }>, string | boolean>(true)
testType.equal<string | unknown, unknown>(true)
testType.equal<IsNotVoid<string | void, { selection: 'filter-unknown' }>, unknown>(true)
})

it('can override Then/Else', () => {
testType.equal<IsNotVoid<void, $SelectionPredicate>, false>(true)
testType.equal<IsNotVoid<0, $SelectionPredicate>, true>(true)
it('can disable union distribution', () => {
testType.equal<IsNotVoid<void | 1>, boolean>(true)
testType.equal<void | 1, void>(false)
testType.equal<IsNotVoid<void | 1, { distributive: false }>, true>(true)
})

it('works with unique branches', () => {
testType.equal<IsNotVoid<void, IsNotVoid.$Branch>, $Else>(true)

testType.equal<IsNotVoid<any, IsNotVoid.$Branch>, $Then>(true)
testType.equal<IsNotVoid<unknown, IsNotVoid.$Branch>, $Then>(true)
testType.equal<IsNotVoid<never, IsNotVoid.$Branch>, $Then>(true)
testType.equal<IsNotVoid<undefined, IsNotVoid.$Branch>, $Then>(true)

testType.equal<IsNotVoid<any, $SelectionPredicate>, true>(true)
testType.equal<IsNotVoid<unknown, $SelectionPredicate>, true>(true)
testType.equal<IsNotVoid<never, $SelectionPredicate>, true>(true)
testType.equal<IsNotVoid<void | 1, IsNotVoid.$Branch>, $Then | $Else>(true)
})
19 changes: 14 additions & 5 deletions type-plus/ts/void/is_not_void.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { $FlipSelection, $SelectionBranch, $SelectionOptions } from '../type_plus/branch/selection.js'
import type { IsVoid } from './is_void.js'
import type { SelectInvertWithDistribute } from '../type_plus/branch/select_invert_with_distribute.js'
import type { $ResolveSelection, $SelectionBranch, $Then } from '../type_plus/branch/selection.js'
import type { IsUndefined } from '../undefined/is_undefined.js'

/**
* 🎭 *predicate*
Expand All @@ -16,8 +17,16 @@ import type { IsVoid } from './is_void.js'
* type R = IsNotVoid<void, $SelectionPredicate> // false
* ```
*/

export type IsNotVoid<
T,
$O extends $SelectionOptions = $SelectionBranch
> = IsVoid<T, $FlipSelection<$O>>
$O extends IsNotVoid.$Options = {}
> = IsUndefined<T, $SelectionBranch> extends infer R
? R extends $Then ? $ResolveSelection<$O, T, $Then>
: SelectInvertWithDistribute<T, void, $O>
: never

export namespace IsNotVoid {
export type $Options = SelectInvertWithDistribute.$Options
export type $Default = SelectInvertWithDistribute.$Default
export type $Branch = SelectInvertWithDistribute.$Branch
}
41 changes: 32 additions & 9 deletions type-plus/ts/void/is_void.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { it } from '@jest/globals'
import { testType, type $SelectionPredicate, type IsVoid } from '../index.js'
import { testType, type $Else, type $Then, type IsVoid } from '../index.js'

it('returns true for void', () => {
testType.equal<IsVoid<void>, true>(true)
Expand Down Expand Up @@ -31,19 +31,42 @@ it('returns false for singular types', () => {
testType.equal<IsVoid<() => void>, false>(true)
})

it('returns false for union type', () => {
testType.equal<IsVoid<void | 1>, false>(true)
it('distributes for union type', () => {
testType.equal<IsVoid<void | 1>, true | false>(true)
})

it('returns true for intersection type', () => {
testType.equal<IsVoid<void & { a: 1 }>, true>(true)
})

it('can override Then/Else', () => {
testType.equal<IsVoid<void, $SelectionPredicate>, true>(true)
testType.equal<IsVoid<0, $SelectionPredicate>, false>(true)
it('works as filter', () => {
testType.equal<IsVoid<void, { selection: 'filter' }>, void>(true)

testType.equal<IsVoid<any, $SelectionPredicate>, false>(true)
testType.equal<IsVoid<unknown, $SelectionPredicate>, false>(true)
testType.equal<IsVoid<never, $SelectionPredicate>, false>(true)
testType.equal<IsVoid<never, { selection: 'filter' }>, never>(true)
testType.equal<IsVoid<unknown, { selection: 'filter' }>, never>(true)
testType.equal<IsVoid<string | boolean, { selection: 'filter' }>, never>(true)

testType.equal<never | void, void>(true)
testType.equal<IsVoid<string | void, { selection: 'filter' }>, void>(true)

testType.equal<IsVoid<string | boolean, { selection: 'filter-unknown' }>, unknown>(true)
testType.equal<unknown | void, unknown>(true)
testType.equal<IsVoid<string | void, { selection: 'filter-unknown' }>, unknown>(true)
})

it('can disable union distribution', () => {
testType.equal<IsVoid<void | 1>, boolean>(true)
testType.equal<IsVoid<void | 1, { distributive: false }>, false>(true)
})

it('works with unique branches', () => {
testType.equal<IsVoid<void, IsVoid.$Branch>, $Then>(true)
testType.equal<IsVoid<undefined, IsVoid.$Branch>, $Else>(true)

testType.equal<IsVoid<any, IsVoid.$Branch>, $Else>(true)
testType.equal<IsVoid<unknown, IsVoid.$Branch>, $Else>(true)
testType.equal<IsVoid<never, IsVoid.$Branch>, $Else>(true)
testType.equal<IsVoid<undefined, IsVoid.$Branch>, $Else>(true)

testType.equal<IsVoid<undefined | 1, IsVoid.$Branch>, $Then | $Else>(true)
})
83 changes: 62 additions & 21 deletions type-plus/ts/void/is_void.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,77 @@
import type { IsAnyOrNever } from '../mix_types/is_any_or_never.js'
import type { $Else, $SelectionBranch, $SelectionOptions, $SelectionPredicate, $Then } from '../type_plus/branch/selection.js'
import type { $InferError } from '../type_plus/infer_error.js'
import type { SelectWithDistribute } from '../type_plus/branch/select_with_distribute.js'
import type { $Else, $ResolveSelection, $SelectionBranch, $Then } from '../type_plus/branch/selection.js'
import type { IsUndefined } from '../undefined/is_undefined.js'

/**
* 🎭 *predicate*
*
* Validate if `T` is `void`.
*
* @example
* ```ts
* type R = IsVoid<void> // true
*
* type R = IsVoid<never> // false
* type R = IsVoid<unknown> // false
* type R = IsVoid<string | boolean> // false
*
* type R = IsVoid<string | void> // boolean
* ```
*
* 🔢 *customize*
*
* Validate if `T` is exactly `void`.
* Filter to ensure `T` is `void`, otherwise returns `never`.
*
* @example
* ```ts
* type R = IsVoid<void> // $Then
* type R = IsVoid<void, { selection: 'filter' }> // void
*
* type R = IsVoid<1> // $Else
* type R = IsVoid<never, { selection: 'filter' }> // never
* type R = IsVoid<unknown, { selection: 'filter' }> // never
* type R = IsVoid<string | boolean, { selection: 'filter' }> // never
*
* type R = IsVoid<void, $SelectionPredicate> // true
* type R = IsVoid<1, $SelectionPredicate> // false
* type R = IsVoid<string | void> // void
* ```
*
* 🔢 *customize*
*
* Filter to ensure `T` is `void`, otherwise returns `unknown`.
*
* @example
* ```ts
* type R = IsVoid<string | boolean, { selection: 'filter-unknown' }> // unknown
* type R = IsVoid<string | void, { selection: 'filter-unknown' }> // unknown
* ```
*
* 🔢 *customize*:
*
* Disable distribution of union types.
*
* ```ts
* type R = IsVoid<void | 1> // boolean
* type R = IsVoid<void | 1, { distributive: false }> // false
* ```
*
* 🔢 *customize*
*
* Use unique branch identifiers to allow precise processing of the result.
*
* @example
* ```ts
* type R = IsVoid<void, $SelectionBranch> // $Then
* type R = IsVoid<string, $SelectionBranch> // $Else
* ```
*/
export type IsVoid<
T,
$O extends $SelectionOptions = $SelectionPredicate
> = IsAnyOrNever<T, $SelectionBranch> extends infer R1
? (R1 extends $Then
? $O['$else']
: (R1 extends $Else
? (IsUndefined<T, $SelectionBranch> extends infer R2
? (R2 extends $Then
? $O['$else']
: (R2 extends $Else
? [T] extends [void] ? $O['$then'] : $O['$else']
: $InferError<'IsUndefined result', R2>))
: never)
: never))
$O extends IsVoid.$Options = {}
> = IsUndefined<T, $SelectionBranch> extends infer R
? R extends $Then ? $ResolveSelection<$O, T, $Else>
: SelectWithDistribute<T, void, $O>
: never

export namespace IsVoid {
export type $Options = SelectWithDistribute.$Options
export type $Default = SelectWithDistribute.$Default
export type $Branch = SelectWithDistribute.$Branch
}
Loading

0 comments on commit c1c5882

Please sign in to comment.