Utilities for compile-time type testing
npm install lay-sing



TypeScript utilities for compile-time type testing and utility types
``ts
// They do nothing at runtime
expect
expect
expect
// Type Error: Property 'success' does not exist on type '{ fail: void; }'.
expect
// ^^^^^^^
`
> [!TIP]
>
> I know this library is quite simple and serves a specific purpose, so one of its API design principles is to minimize the cognitive load for users. You just need to remember to start with an expect<>() call and end with some property access. Leave the rest to editor suggestions and inline documentation.
NPM
`sh`
npm i -D lay-sing
`ts`
import { expect } from 'lay-sing'
Deno
`sh`
deno add npm:lay-sing
`ts`
import { expect } from 'lay-sing'
This library is also published to JSR (@leawind/lay-sing)
`sh`
deno add @leawind/lay-sing
`ts`
import { expect } from '@leawind/lay-sing'
`ts`
import { expect } from 'https://raw.githubusercontent.com/Leawind/lay-sing/refs/heads/main/src/main/index.ts'
import { Exact } from 'https://raw.githubusercontent.com/Leawind/lay-sing/refs/heads/main/src/utils/index.ts'
---
`ts`
import { expect } from 'lay-sing'
The main module provides utilities for compile-time type validation. These utilities have no runtime impact — they always return a special NOOP value that safely supports almost any property access or method call.
A typical type test statement follows this pattern:
`ts`
expect
- It starts with a function call like expect or compare.success
- It ends with a property like or .fail
- Type error occurs only if the assertion fails
> [!CAUTION]
>
> Only statements ending with property access are type assertions. Without property access, type error may never occur:
>
> `diff`
> - expect
> + expect
>
At runtime, the function always returns the NOOP object, which performs no operation. It can be accessed, called, or chained indefinitely without throwing errors.
#### Common Usage
`ts
// Passes only if A and B are identical
expect
// Passes if A extends B
expect<12138>().toExtend
// Passes if mutually assignable
expect<{ a: 1; b: 2 }>().toEqual<{ a: 1 } & { b: 2 }>().success
// Test property existence
expect<{ name: string }>().toHaveKey<'name'>().success
`
Aliases:
`ts
expect
expect
expect<'hello'>().toExtend
expect<'hello'>().toExtendString
`
#### NOOP
A Proxy-based no-op object with the following behavior:
- Most property/method accesses return the NOOP object itself.
- .toString(), .valueOf() returns string "[NOOP]".then
- Not thenable ( is undefined).
It's used as returned value of expect() and compare().
`ts`
expect().foo.bar().baz.qux // Safe, returns NOOP
String(NOOP) // "[NOOP]"
await NOOP // Does not await (not thenable)
It provides some utility types organized into categories for common type-level programming tasks. These can be imported from the lay-sing/utils entry point.
`ts`
import type { Exact, Extends, Overlap } from 'lay-sing/utils'
`typescript
// Import the utility types
import type { ConcatTuple, Exact, If, KeysOfBaseType } from '@leawind/lay-sing/utils'
// Test if exactly the same
type False = Exact<{ a: 1 }, { a?: 1 }> // false
type Yes = Exact
// Conditional Types
type Result = If
type FailResult = If
// Tuple Manipulation
type Combined = ConcatTuple<[1, 2], [3, 4]> // [1, 2, 3, 4]
type UniqueCombined = ConcatUniqueTuple<[1, 2], [2, 3]> // [1, 2, 3]
``