Skip to Content
Patterns GuideP Not Negation Patterns

Last Updated: 3/18/2026


P.not() - Negation Patterns

P.not() creates a pattern that matches everything except the specified value or pattern. It’s useful for excluding specific cases.

Basic Usage

import { match, P } from 'ts-pattern'; type Input = string | number | boolean; const toNumber = (input: Input) => match(input) .with(P.not(P.boolean), (n) => n) // n: string | number .with(true, () => 1) .with(false, () => 0) .exhaustive();

Signature

P.not<P extends Pattern>(pattern: P): Pattern

Examples

Negating Primitives

// Match anything except 'loading' match(status) .with(P.not('loading'), (status) => { // status: Exclude<Status, 'loading'> console.log('Not loading:', status); }) .with('loading', () => 'Loading...') .exhaustive();

Negating Types

type Input = string | number | null; match(input) .with(P.not(P.nullish), (value) => { // value: string | number console.log('Has value:', value); }) .with(P.nullish, () => 'No value') .exhaustive();

Negating Object Patterns

type State = | { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: string } | { status: 'error'; error: Error }; match(state) .with( { status: P.not('loading') }, (state) => { // state: { status: 'idle' } | { status: 'success' | 'error', ... } console.log('Not loading'); } ) .otherwise(() => 'Loading');

Excluding Multiple Values

Use P.union inside P.not to exclude multiple values:

match(status) .with( P.not(P.union('idle', 'loading')), (status) => { // status: Exclude<Status, 'idle' | 'loading'> console.log('Active status:', status); } ) .otherwise(() => 'Inactive');

Real-World Examples

State Machine Transitions

type State = | { status: 'idle' } | { status: 'loading'; startTime: number } | { status: 'success'; data: string } | { status: 'error'; error: Error }; type Event = { type: 'fetch' } | { type: 'cancel' }; const reducer = (state: State, event: Event) => match([state, event]) .with( [{ status: P.not('loading') }, { type: 'fetch' }], () => ({ status: 'loading' as const, startTime: Date.now() }) ) .with( [{ status: 'loading' }, { type: 'cancel' }], () => ({ status: 'idle' as const }) ) .otherwise(() => state);

Form Validation

type FormField = | { type: 'text'; value: string } | { type: 'number'; value: number } | { type: 'checkbox'; value: boolean } | { type: 'file'; value: File }; const validateRequired = (field: FormField) => match(field) .with( { type: P.not('checkbox'), value: '' }, () => 'This field is required' ) .with( { type: P.not('checkbox'), value: P.not('') }, () => null // Valid ) .with( { type: 'checkbox' }, (field) => null // Checkboxes have different validation ) .exhaustive();

Permission Checks

type User = { role: 'admin' | 'editor' | 'viewer'; verified: boolean; }; const canEdit = (user: User) => match(user) .with( { role: P.not('viewer'), verified: true }, () => true ) .otherwise(() => false); const canDelete = (user: User) => match(user) .with( { role: P.not(P.union('viewer', 'editor')), verified: true }, () => true ) .otherwise(() => false);

Combining with Other Patterns

P.not with P.when

match(user) .with( { age: P.not(P.when((age) => age < 18)), verified: true }, (user) => 'Adult verified user' ) .otherwise(() => 'Minor or unverified');

P.not with P.select

match(response) .with( { status: P.not('loading'), data: P.select() }, (data) => { // status is not 'loading', data is selected return processData(data); } ) .otherwise(() => null);

Nested P.not

match(obj) .with( { status: P.not('error'), config: { mode: P.not('debug') } }, () => 'Production mode' ) .otherwise(() => 'Error or debug mode');

Type Safety

TypeScript accurately narrows types when using P.not:

type Status = 'idle' | 'loading' | 'success' | 'error'; declare const status: Status; match(status) .with(P.not('loading'), (s) => { // ✅ s: 'idle' | 'success' | 'error' console.log(s); }) .exhaustive();

Common Pitfalls

Double Negation

Avoid using P.not(P.not(...)) as it’s confusing. Just use the positive pattern:

// ❌ Don't do this .with(P.not(P.not('loading')), ...) // ✅ Do this instead .with('loading', ...)

P.not with Arrays

P.not works with array patterns but be careful with type inference:

// Matches arrays that don't start with 'error' match(arr) .with([P.not('error'), ...P.array()], ...) .otherwise(...);