Ts.Inhabitance
Type utilities for detecting TypeScript edge case types: any, never, and unknown.
These utilities are useful for conditional type logic that needs to handle these special types differently.
Import
import { Ts } from '@wollybeard/kit'
// Access via namespace
Ts.Inhabitanceimport { Inhabitance } from '@wollybeard/kit/ts'Namespaces
| Namespace | Description |
|---|---|
Case | — |
Types
[T] IsNever
type IsNever<$Type> = [$Type] extends [never] ? true : falseType utilities for detecting TypeScript edge case types: any, never, and unknown.
These utilities are useful for conditional type logic that needs to handle these special types differently.
[T] IsAny
type IsAny<T> = 0 extends 1 & T ? true : falseCheck if a type is any.
Uses the fact that any is the only type where 0 extends (1 & T) is true, since any absorbs all type operations including impossible intersections.
Examples:
type _ = Ts.Test.Cases<
Ts.Test.equal<IsAny<any>, true>,
Ts.Test.equal<IsAny<unknown>, false>,
Ts.Test.equal<IsAny<string>, false>,
Ts.Test.equal<IsAny<never>, false>
>[T] IsUnknown
type IsUnknown<T> = unknown extends T ? (IsAny<T> extends true ? false : true)
: falseCheck if a type is unknown.
Unknown is the top type
- everything extends unknown (except any, which is special). So we check if unknown extends the type (only true for unknown and any), then exclude any using IsAny.
Examples:
type _ = Ts.Test.Cases<
Ts.Test.equal<IsUnknown<unknown>, true>,
Ts.Test.equal<IsUnknown<any>, false>,
Ts.Test.equal<IsUnknown<string>, false>,
Ts.Test.equal<IsUnknown<never>, false>
>[T] IsAnyOrUnknown
type IsAnyOrUnknown<T> = unknown extends T ? true : falseDetect if a type is any or unknown.
Examples:
type _ = Ts.Test.Cases<
Ts.Test.equal<IsAnyOrUnknown<any>, true>,
Ts.Test.equal<IsAnyOrUnknown<unknown>, true>,
Ts.Test.equal<IsAnyOrUnknown<never>, false>,
Ts.Test.equal<IsAnyOrUnknown<string>, false>
>[T] IsAnyOrUnknownOrNever
type IsAnyOrUnknownOrNever<T> = [T] extends [never] ? true /* never */
: unknown extends T ? true /* any or unknown, we don't care which */
: falseDetect if a type is any, unknown, or never.
Examples:
type _ = Ts.Test.Cases<
Ts.Test.equal<IsAnyOrUnknownOrNever<any>, true>,
Ts.Test.equal<IsAnyOrUnknownOrNever<unknown>, true>,
Ts.Test.equal<IsAnyOrUnknownOrNever<never>, true>,
Ts.Test.equal<IsAnyOrUnknownOrNever<string>, false>
>[T] GetCase
type GetCase<T> = [T] extends [never] ? Case.Never
: unknown extends T ? (
0 extends (1 & T) ? Case.Any
: Case.Unknown
)
: Case.Proper[U] Case
type Case =
| Case.Any
| Case.Unknown
| Case.Never
| Case.Proper[T] IsEmpty
type IsEmpty<$T> = $T extends readonly [] ? true
: $T extends '' ? true
: $T extends object ? keyof $T extends never ? true : false
: falseCheck if a type is empty.
Empty types:
- Empty array:
[]orreadonly [] - Empty object:
keyof T extends never(no properties) - Empty string:
''
Note: {} and interface Foo {} mean "non-nullish", NOT empty!
Examples:
type _ = Ts.Test.Cases<
Ts.Test.equal<IsEmpty<[]>, true>,
Ts.Test.equal<IsEmpty<readonly []>, true>,
Ts.Test.equal<IsEmpty<''>, true>,
Ts.Test.equal<IsEmpty<Record<string, never>>, true>,
Ts.Test.equal<IsEmpty<[1]>, false>,
Ts.Test.equal<IsEmpty<'hello'>, false>,
Ts.Test.equal<IsEmpty<{ a: 1 }>, false>,
Ts.Test.equal<IsEmpty<{}>, false> // {} = non-nullish, not empty!
>