@rightcapital/assert 0.0.0 → 2.1.3-feature-assertion-helpers-api-refinement.2357.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +146 -1
- package/lib/index.d.ts +166 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +205 -0
- package/lib/index.js.map +1 -0
- package/package.json +32 -4
- package/skills/assert/SKILL.md +122 -0
- package/src/index.ts +213 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 RightCapital
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,148 @@
|
|
|
1
1
|
# @rightcapital/assert
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Type-safe assertion utilities for defensive programming in TypeScript applications.
|
|
4
|
+
|
|
5
|
+
## Key Features
|
|
6
|
+
|
|
7
|
+
- **Compile-Time Exhaustiveness (`assertExhaustive`)**: Enforce full coverage of union types at compile time with TypeScript `never`.
|
|
8
|
+
- **Control-Flow Guard (`assertUnreachable`)**: Mark unreachable branches for control-flow analysis and runtime safety.
|
|
9
|
+
- **Expression-Level Assertions (`ensure` / `ensureNonNullable`)**: Assert and return narrowed values in single-line expressions.
|
|
10
|
+
- **Type Narrowing (`assert` / `assertNonNullable`)**: Narrow types with standard TypeScript assertion signatures.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @rightcapital/assert
|
|
16
|
+
# or
|
|
17
|
+
pnpm add @rightcapital/assert
|
|
18
|
+
# or
|
|
19
|
+
yarn add @rightcapital/assert
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage Examples
|
|
23
|
+
|
|
24
|
+
### Compile-Time Exhaustiveness Checking (`assertExhaustive`)
|
|
25
|
+
|
|
26
|
+
Use `assertExhaustive` in `switch` statements or `if-else` chains. It leverages TypeScript `never` to catch missing cases at compile time.
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { assertExhaustive } from '@rightcapital/assert';
|
|
30
|
+
|
|
31
|
+
type Action = { type: 'open' } | { type: 'close' };
|
|
32
|
+
|
|
33
|
+
function handleAction(action: Action) {
|
|
34
|
+
switch (action.type) {
|
|
35
|
+
case 'open':
|
|
36
|
+
return 'Opening';
|
|
37
|
+
case 'close':
|
|
38
|
+
return 'Closing';
|
|
39
|
+
default:
|
|
40
|
+
// If a new Action type is added without a case,
|
|
41
|
+
// TypeScript reports a compile error here.
|
|
42
|
+
return assertExhaustive(action);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Control Flow & Unreachable Code Guard (`assertUnreachable`)
|
|
48
|
+
|
|
49
|
+
Use `assertUnreachable` for code branches that should never execute. Because `assertUnreachable` returns `never`, TypeScript understands control flow stops at this point.
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { assertUnreachable } from '@rightcapital/assert';
|
|
53
|
+
|
|
54
|
+
function processStatus(status: 'active' | 'inactive') {
|
|
55
|
+
if (status === 'active') {
|
|
56
|
+
handleActive();
|
|
57
|
+
} else if (status === 'inactive') {
|
|
58
|
+
handleInactive();
|
|
59
|
+
} else {
|
|
60
|
+
// TypeScript knows execution stops here because `assertUnreachable` returns `never`.
|
|
61
|
+
assertUnreachable(`Unexpected status: ${status}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
> **Tip**: If your project enforces the ESLint `consistent-return` rule, add `return` before the function call (for example, `return assertUnreachable(...)`) to satisfy ESLint.
|
|
67
|
+
|
|
68
|
+
### Expression-Level Type Narrowing (`ensure` & `ensureNonNullable`)
|
|
69
|
+
|
|
70
|
+
Unlike `assert`, `ensure` and `ensureNonNullable` validate a value and return it with a narrowed type. This allows assertions inside single-line expressions or method chains.
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { ensure, ensureNonNullable } from '@rightcapital/assert';
|
|
74
|
+
|
|
75
|
+
// Assert and get a non-nullable value in one expression
|
|
76
|
+
const userName = ensureNonNullable(getUser(), 'User must exist').name;
|
|
77
|
+
|
|
78
|
+
// Assert with a custom type guard
|
|
79
|
+
const admin = ensure(currentUser, isAdminUser, 'Admin privileges required');
|
|
80
|
+
// `admin` is typed as AdminUser
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Basic Assertion & Type Narrowing (`assert` & `assertNonNullable`)
|
|
84
|
+
|
|
85
|
+
Use `assert` and `assertNonNullable` for standalone statement assertions.
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { assert, assertNonNullable } from '@rightcapital/assert';
|
|
89
|
+
|
|
90
|
+
// Basic condition assertion
|
|
91
|
+
assert(user.age >= 18, 'User must be at least 18 years old');
|
|
92
|
+
|
|
93
|
+
// Non-nullable assertion with type narrowing
|
|
94
|
+
assertNonNullable(user, 'User cannot be null');
|
|
95
|
+
// `user` is narrowed to NonNullable<User>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Error Handling (`AssertError`)
|
|
99
|
+
|
|
100
|
+
All assertion functions throw `AssertError` (which extends `Error`) when an assertion fails.
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { assert, AssertError } from '@rightcapital/assert';
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
assert(user.age >= 18, 'User must be at least 18 years old');
|
|
107
|
+
} catch (error) {
|
|
108
|
+
if (error instanceof AssertError) {
|
|
109
|
+
console.error('Assertion failed:', error.message);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## API Summary
|
|
115
|
+
|
|
116
|
+
| API | Return / Type Behavior | Typical Use Case |
|
|
117
|
+
| :------------------------------- | :-------------------------------- | :---------------------------------------------------- |
|
|
118
|
+
| `AssertError` | `class extends Error` | Error thrown on assertion failure |
|
|
119
|
+
| `assertExhaustive(value, msg?)` | `never` | Enforce exhaustiveness check in `switch` or `if-else` |
|
|
120
|
+
| `assertUnreachable(msg?)` | `never` | Mark logically unreachable code branches |
|
|
121
|
+
| `ensure(value, predicate, msg?)` | `S extends T` | Validate and return value with narrowed type |
|
|
122
|
+
| `ensureNonNullable(value, msg?)` | `NonNullable<T>` | Validate non-null/undefined and return value |
|
|
123
|
+
| `assert(value, msg?)` | `asserts value` | Standard boolean condition assertion |
|
|
124
|
+
| `assertNonNullable(value, msg?)` | `asserts value is NonNullable<T>` | Standard non-null/undefined assertion |
|
|
125
|
+
|
|
126
|
+
## Agent Skills
|
|
127
|
+
|
|
128
|
+
This package ships with an [Agent Skill](https://agentskills.io) that teaches AI coding agents (Claude Code, Cursor, etc.) how to use the assert API correctly, following the [npm-based Agent Skills Convention](https://github.com/antfu/skills-npm).
|
|
129
|
+
|
|
130
|
+
### Automatic discovery (recommended)
|
|
131
|
+
|
|
132
|
+
If your project uses [`skills-npm`](https://github.com/antfu/skills-npm), the skill is discovered automatically from `node_modules`:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npx skills-npm
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Manual installation
|
|
139
|
+
|
|
140
|
+
You can also install the skill directly using the [`skills` CLI](https://github.com/vercel-labs/skills):
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
npx skills add https://github.com/RightCapitalHQ/frontend-libraries/tree/main/packages/assert/skills
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## API Reference
|
|
147
|
+
|
|
148
|
+
See the [generated documentation](./docs/README.md) for detailed API reference.
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assertion functions provide type-safe assertions and validation.
|
|
3
|
+
* When assertions fail, they throw `AssertError`.
|
|
4
|
+
*
|
|
5
|
+
* @author lixiaoyan <lxy.lixiaoyan@gmail.com>
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* // Basic assertion
|
|
9
|
+
* assert(user.age >= 18, 'User must be at least 18 years old');
|
|
10
|
+
*
|
|
11
|
+
* // Non-nullable assertion with type narrowing
|
|
12
|
+
* assertNonNullable(user, 'User cannot be null');
|
|
13
|
+
* // user is now typed as NonNullable<T>
|
|
14
|
+
*
|
|
15
|
+
* // Ensure with type guard
|
|
16
|
+
* const admin = ensure(currentUser, isAdminUser, 'Admin required');
|
|
17
|
+
* // admin is now typed as AdminUser
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Error thrown when an assertion fails.
|
|
22
|
+
*/
|
|
23
|
+
export declare class AssertError extends Error {
|
|
24
|
+
readonly name = "AssertError";
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Basic assertion: verifies that a value or expression is `true`, otherwise throws an exception.
|
|
28
|
+
*
|
|
29
|
+
* @param value - The value to assert as truthy
|
|
30
|
+
* @param message - Optional custom error message
|
|
31
|
+
* @throws {AssertError} Throws an error if `value` is not `true`.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* // Basic usage
|
|
36
|
+
* assert(user.age >= 18, 'User must be at least 18 years old');
|
|
37
|
+
*
|
|
38
|
+
* // Condition validation
|
|
39
|
+
* const isValid = validateData(data);
|
|
40
|
+
* assert(isValid, 'Data validation failed');
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare function assert(value: unknown, message?: string): asserts value;
|
|
44
|
+
/**
|
|
45
|
+
* Asserts that a value is not `null` or `undefined`, providing TypeScript type narrowing to `NonNullable<T>`.
|
|
46
|
+
* Ensures subsequent code can safely access the value.
|
|
47
|
+
*
|
|
48
|
+
* @param value - The value to check for null/undefined
|
|
49
|
+
* @param message - Optional custom error message
|
|
50
|
+
* @throws {AssertError} Throws an error if `value` is `null` or `undefined`.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* function processUser(user: User | null | undefined) {
|
|
55
|
+
* assertNonNullable(user, 'User cannot be null');
|
|
56
|
+
* // user is now typed as User
|
|
57
|
+
* console.log(user.name); // Safe to access
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare function assertNonNullable<T>(value: T, message?: string): asserts value is NonNullable<T>;
|
|
62
|
+
/**
|
|
63
|
+
* Similar to `assert`, but returns the value. Ensures a value matches a type predicate and returns it with narrowed type.
|
|
64
|
+
*
|
|
65
|
+
* @param value - The value to validate
|
|
66
|
+
* @param predicate - Type guard function that validates the value
|
|
67
|
+
* @param message - Optional custom error message
|
|
68
|
+
* @returns The value with narrowed type
|
|
69
|
+
* @throws {AssertError} Throws an error if `predicate` returns `false`.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* // Define type guard
|
|
74
|
+
* function isAdminUser(user: User): user is AdminUser {
|
|
75
|
+
* return user.role === 'admin';
|
|
76
|
+
* }
|
|
77
|
+
*
|
|
78
|
+
* // Use ensure to get type-safe value
|
|
79
|
+
* const admin = ensure(
|
|
80
|
+
* currentUser,
|
|
81
|
+
* isAdminUser,
|
|
82
|
+
* 'Admin privileges required'
|
|
83
|
+
* );
|
|
84
|
+
* // admin is typed as AdminUser
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export declare function ensure<T, S extends T>(value: T, predicate: (value: T) => value is S, message?: string): S;
|
|
88
|
+
/**
|
|
89
|
+
* Similar to `assertNonNullable`, but returns the value. Ensures a value is not null/undefined and returns it.
|
|
90
|
+
*
|
|
91
|
+
* @param value - The value to check for null/undefined
|
|
92
|
+
* @param message - Optional custom error message
|
|
93
|
+
* @returns The non-nullable value
|
|
94
|
+
* @throws {AssertError} Throws an error if `value` is `null` or `undefined`.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* // Use in expressions
|
|
99
|
+
* const config = ensureNonNullable(
|
|
100
|
+
* getConfig(),
|
|
101
|
+
* 'Configuration not found'
|
|
102
|
+
* );
|
|
103
|
+
*
|
|
104
|
+
* // Chain calls
|
|
105
|
+
* const userName = ensureNonNullable(user, 'User not found').name;
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export declare function ensureNonNullable<T>(value: T, message?: string): NonNullable<T>;
|
|
109
|
+
/**
|
|
110
|
+
* Marks code branches that should theoretically never be reached.
|
|
111
|
+
* Used for defensive programming to prevent unexpected code execution when data or logic doesn't match expectations.
|
|
112
|
+
*
|
|
113
|
+
* @param message - Optional custom error message
|
|
114
|
+
* @returns Never returns (always throws)
|
|
115
|
+
* @throws {AssertError} Always thrown.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* function processStatus(status: 'active' | 'inactive' | 'activating') {
|
|
120
|
+
* if (status === 'active') {
|
|
121
|
+
* // Handle active state
|
|
122
|
+
* } else if (status === 'inactive') {
|
|
123
|
+
* // Handle inactive state
|
|
124
|
+
* } else {
|
|
125
|
+
* // According to business logic, this should never be reached
|
|
126
|
+
* assertUnreachable(`Unexpected status value: ${status}`);
|
|
127
|
+
* }
|
|
128
|
+
* }
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
export declare function assertUnreachable(message?: string): never;
|
|
132
|
+
/**
|
|
133
|
+
* Used for exhaustiveness checking of union types. Ensures switch or if-else statements cover all possible types.
|
|
134
|
+
* Leverages TypeScript's `never` type to catch missing branches at compile time.
|
|
135
|
+
*
|
|
136
|
+
* @param value - The value that should be `never` if all cases are handled
|
|
137
|
+
* @param message - Optional custom error message
|
|
138
|
+
* @returns Never returns (always throws)
|
|
139
|
+
* @throws {AssertError} Always thrown.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* type Action =
|
|
144
|
+
* | { type: 'ADD'; payload: number }
|
|
145
|
+
* | { type: 'SUBTRACT'; payload: number }
|
|
146
|
+
* | { type: 'MULTIPLY'; payload: number };
|
|
147
|
+
*
|
|
148
|
+
* function reducer(action: Action) {
|
|
149
|
+
* switch (action.type) {
|
|
150
|
+
* case 'ADD':
|
|
151
|
+
* return state + action.payload;
|
|
152
|
+
* case 'SUBTRACT':
|
|
153
|
+
* return state - action.payload;
|
|
154
|
+
* case 'MULTIPLY':
|
|
155
|
+
* return state * action.payload;
|
|
156
|
+
* default:
|
|
157
|
+
* // If all types are exhausted, action is of type never
|
|
158
|
+
* // If new Action types are added but not handled, TypeScript will report error
|
|
159
|
+
* return assertExhaustive(action);
|
|
160
|
+
* // ^ never
|
|
161
|
+
* }
|
|
162
|
+
* }
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
export declare function assertExhaustive(value: never, message?: string): never;
|
|
166
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;GAEG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC,SAAyB,IAAI,iBAAiB;CAC/C;AAaD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAItE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,KAAK,EAAE,CAAC,EACR,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,KAAK,IAAI,WAAW,CAAC,CAAC,CAAC,CAIjC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,EACnC,KAAK,EAAE,CAAC,EACR,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,KAAK,IAAI,CAAC,EACnC,OAAO,CAAC,EAAE,MAAM,GACf,CAAC,CAGH;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,KAAK,EAAE,CAAC,EACR,OAAO,CAAC,EAAE,MAAM,GACf,WAAW,CAAC,CAAC,CAAC,CAKhB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAEzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAEtE"}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Assertion functions provide type-safe assertions and validation.
|
|
4
|
+
* When assertions fail, they throw `AssertError`.
|
|
5
|
+
*
|
|
6
|
+
* @author lixiaoyan <lxy.lixiaoyan@gmail.com>
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* // Basic assertion
|
|
10
|
+
* assert(user.age >= 18, 'User must be at least 18 years old');
|
|
11
|
+
*
|
|
12
|
+
* // Non-nullable assertion with type narrowing
|
|
13
|
+
* assertNonNullable(user, 'User cannot be null');
|
|
14
|
+
* // user is now typed as NonNullable<T>
|
|
15
|
+
*
|
|
16
|
+
* // Ensure with type guard
|
|
17
|
+
* const admin = ensure(currentUser, isAdminUser, 'Admin required');
|
|
18
|
+
* // admin is now typed as AdminUser
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.assertExhaustive = exports.assertUnreachable = exports.ensureNonNullable = exports.ensure = exports.assertNonNullable = exports.assert = exports.AssertError = void 0;
|
|
23
|
+
/**
|
|
24
|
+
* Error thrown when an assertion fails.
|
|
25
|
+
*/
|
|
26
|
+
class AssertError extends Error {
|
|
27
|
+
constructor() {
|
|
28
|
+
super(...arguments);
|
|
29
|
+
this.name = 'AssertError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.AssertError = AssertError;
|
|
33
|
+
/**
|
|
34
|
+
* Throws an AssertError for failed assertions.
|
|
35
|
+
*/
|
|
36
|
+
function throwError(name, value, message) {
|
|
37
|
+
throw new AssertError(message !== null && message !== void 0 ? message : `${name}: Unexpected ${String(value)}`);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Basic assertion: verifies that a value or expression is `true`, otherwise throws an exception.
|
|
41
|
+
*
|
|
42
|
+
* @param value - The value to assert as truthy
|
|
43
|
+
* @param message - Optional custom error message
|
|
44
|
+
* @throws {AssertError} Throws an error if `value` is not `true`.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* // Basic usage
|
|
49
|
+
* assert(user.age >= 18, 'User must be at least 18 years old');
|
|
50
|
+
*
|
|
51
|
+
* // Condition validation
|
|
52
|
+
* const isValid = validateData(data);
|
|
53
|
+
* assert(isValid, 'Data validation failed');
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
function assert(value, message) {
|
|
57
|
+
if (value !== true) {
|
|
58
|
+
throwError('assert', value, message);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.assert = assert;
|
|
62
|
+
/**
|
|
63
|
+
* Asserts that a value is not `null` or `undefined`, providing TypeScript type narrowing to `NonNullable<T>`.
|
|
64
|
+
* Ensures subsequent code can safely access the value.
|
|
65
|
+
*
|
|
66
|
+
* @param value - The value to check for null/undefined
|
|
67
|
+
* @param message - Optional custom error message
|
|
68
|
+
* @throws {AssertError} Throws an error if `value` is `null` or `undefined`.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* function processUser(user: User | null | undefined) {
|
|
73
|
+
* assertNonNullable(user, 'User cannot be null');
|
|
74
|
+
* // user is now typed as User
|
|
75
|
+
* console.log(user.name); // Safe to access
|
|
76
|
+
* }
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
function assertNonNullable(value, message) {
|
|
80
|
+
if (value === null || value === undefined) {
|
|
81
|
+
throwError('assertNonNullable', value, message);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
exports.assertNonNullable = assertNonNullable;
|
|
85
|
+
/**
|
|
86
|
+
* Similar to `assert`, but returns the value. Ensures a value matches a type predicate and returns it with narrowed type.
|
|
87
|
+
*
|
|
88
|
+
* @param value - The value to validate
|
|
89
|
+
* @param predicate - Type guard function that validates the value
|
|
90
|
+
* @param message - Optional custom error message
|
|
91
|
+
* @returns The value with narrowed type
|
|
92
|
+
* @throws {AssertError} Throws an error if `predicate` returns `false`.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* // Define type guard
|
|
97
|
+
* function isAdminUser(user: User): user is AdminUser {
|
|
98
|
+
* return user.role === 'admin';
|
|
99
|
+
* }
|
|
100
|
+
*
|
|
101
|
+
* // Use ensure to get type-safe value
|
|
102
|
+
* const admin = ensure(
|
|
103
|
+
* currentUser,
|
|
104
|
+
* isAdminUser,
|
|
105
|
+
* 'Admin privileges required'
|
|
106
|
+
* );
|
|
107
|
+
* // admin is typed as AdminUser
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
function ensure(value, predicate, message) {
|
|
111
|
+
assert(predicate(value), message);
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
exports.ensure = ensure;
|
|
115
|
+
/**
|
|
116
|
+
* Similar to `assertNonNullable`, but returns the value. Ensures a value is not null/undefined and returns it.
|
|
117
|
+
*
|
|
118
|
+
* @param value - The value to check for null/undefined
|
|
119
|
+
* @param message - Optional custom error message
|
|
120
|
+
* @returns The non-nullable value
|
|
121
|
+
* @throws {AssertError} Throws an error if `value` is `null` or `undefined`.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* // Use in expressions
|
|
126
|
+
* const config = ensureNonNullable(
|
|
127
|
+
* getConfig(),
|
|
128
|
+
* 'Configuration not found'
|
|
129
|
+
* );
|
|
130
|
+
*
|
|
131
|
+
* // Chain calls
|
|
132
|
+
* const userName = ensureNonNullable(user, 'User not found').name;
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
function ensureNonNullable(value, message) {
|
|
136
|
+
if (value === null || value === undefined) {
|
|
137
|
+
throwError('ensureNonNullable', value, message);
|
|
138
|
+
}
|
|
139
|
+
return value;
|
|
140
|
+
}
|
|
141
|
+
exports.ensureNonNullable = ensureNonNullable;
|
|
142
|
+
/**
|
|
143
|
+
* Marks code branches that should theoretically never be reached.
|
|
144
|
+
* Used for defensive programming to prevent unexpected code execution when data or logic doesn't match expectations.
|
|
145
|
+
*
|
|
146
|
+
* @param message - Optional custom error message
|
|
147
|
+
* @returns Never returns (always throws)
|
|
148
|
+
* @throws {AssertError} Always thrown.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* function processStatus(status: 'active' | 'inactive' | 'activating') {
|
|
153
|
+
* if (status === 'active') {
|
|
154
|
+
* // Handle active state
|
|
155
|
+
* } else if (status === 'inactive') {
|
|
156
|
+
* // Handle inactive state
|
|
157
|
+
* } else {
|
|
158
|
+
* // According to business logic, this should never be reached
|
|
159
|
+
* assertUnreachable(`Unexpected status value: ${status}`);
|
|
160
|
+
* }
|
|
161
|
+
* }
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
function assertUnreachable(message) {
|
|
165
|
+
throwError('assertUnreachable', null, message);
|
|
166
|
+
}
|
|
167
|
+
exports.assertUnreachable = assertUnreachable;
|
|
168
|
+
/**
|
|
169
|
+
* Used for exhaustiveness checking of union types. Ensures switch or if-else statements cover all possible types.
|
|
170
|
+
* Leverages TypeScript's `never` type to catch missing branches at compile time.
|
|
171
|
+
*
|
|
172
|
+
* @param value - The value that should be `never` if all cases are handled
|
|
173
|
+
* @param message - Optional custom error message
|
|
174
|
+
* @returns Never returns (always throws)
|
|
175
|
+
* @throws {AssertError} Always thrown.
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```typescript
|
|
179
|
+
* type Action =
|
|
180
|
+
* | { type: 'ADD'; payload: number }
|
|
181
|
+
* | { type: 'SUBTRACT'; payload: number }
|
|
182
|
+
* | { type: 'MULTIPLY'; payload: number };
|
|
183
|
+
*
|
|
184
|
+
* function reducer(action: Action) {
|
|
185
|
+
* switch (action.type) {
|
|
186
|
+
* case 'ADD':
|
|
187
|
+
* return state + action.payload;
|
|
188
|
+
* case 'SUBTRACT':
|
|
189
|
+
* return state - action.payload;
|
|
190
|
+
* case 'MULTIPLY':
|
|
191
|
+
* return state * action.payload;
|
|
192
|
+
* default:
|
|
193
|
+
* // If all types are exhausted, action is of type never
|
|
194
|
+
* // If new Action types are added but not handled, TypeScript will report error
|
|
195
|
+
* return assertExhaustive(action);
|
|
196
|
+
* // ^ never
|
|
197
|
+
* }
|
|
198
|
+
* }
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
function assertExhaustive(value, message) {
|
|
202
|
+
throwError('assertExhaustive', value, message);
|
|
203
|
+
}
|
|
204
|
+
exports.assertExhaustive = assertExhaustive;
|
|
205
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;GAkBG;;;AAEH;;GAEG;AACH,MAAa,WAAY,SAAQ,KAAK;IAAtC;;QAC2B,SAAI,GAAG,aAAa,CAAC;IAChD,CAAC;CAAA;AAFD,kCAEC;AAED;;GAEG;AACH,SAAS,UAAU,CACjB,IAAY,EACZ,KAAc,EACd,OAA2B;IAE3B,MAAM,IAAI,WAAW,CAAC,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,GAAG,IAAI,gBAAgB,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAC3E,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,SAAgB,MAAM,CAAC,KAAc,EAAE,OAAgB;IACrD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,UAAU,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC;AACH,CAAC;AAJD,wBAIC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,SAAgB,iBAAiB,CAC/B,KAAQ,EACR,OAAgB;IAEhB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,UAAU,CAAC,mBAAmB,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IAClD,CAAC;AACH,CAAC;AAPD,8CAOC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,SAAgB,MAAM,CACpB,KAAQ,EACR,SAAmC,EACnC,OAAgB;IAEhB,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;IAClC,OAAO,KAAK,CAAC;AACf,CAAC;AAPD,wBAOC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAgB,iBAAiB,CAC/B,KAAQ,EACR,OAAgB;IAEhB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,UAAU,CAAC,mBAAmB,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AARD,8CAQC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,SAAgB,iBAAiB,CAAC,OAAgB;IAChD,UAAU,CAAC,mBAAmB,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AACjD,CAAC;AAFD,8CAEC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,SAAgB,gBAAgB,CAAC,KAAY,EAAE,OAAgB;IAC7D,UAAU,CAAC,kBAAkB,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;AACjD,CAAC;AAFD,4CAEC"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rightcapital/assert",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.1.3-feature-assertion-helpers-api-refinement.2357.1.0",
|
|
4
|
+
"description": "Type-safe assertion utilities for defensive programming.",
|
|
5
|
+
"author": "RightCapital Ecosystem team <npm-publisher@rightcapital.com>",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"assertion",
|
|
8
|
+
"type-safe",
|
|
9
|
+
"validation",
|
|
10
|
+
"defensive",
|
|
11
|
+
"TypeScript",
|
|
12
|
+
"tanstack-intent",
|
|
13
|
+
"utilities"
|
|
14
|
+
],
|
|
15
|
+
"sideEffects": false,
|
|
4
16
|
"license": "MIT",
|
|
17
|
+
"main": "lib/index.js",
|
|
18
|
+
"typings": "lib/index.d.ts",
|
|
5
19
|
"repository": {
|
|
6
20
|
"type": "git",
|
|
7
21
|
"url": "git+https://github.com/RightCapitalHQ/frontend-libraries.git",
|
|
8
22
|
"directory": "packages/assert"
|
|
9
23
|
},
|
|
24
|
+
"directories": {
|
|
25
|
+
"lib": "lib"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"lib",
|
|
29
|
+
"skills",
|
|
30
|
+
"src"
|
|
31
|
+
],
|
|
10
32
|
"publishConfig": {
|
|
11
|
-
"registry": "https://registry.npmjs.org"
|
|
12
|
-
|
|
33
|
+
"registry": "https://registry.npmjs.org"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"typedoc": "0.25.7",
|
|
37
|
+
"typedoc-plugin-markdown": "3.17.1"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"docs": "pnpm exec typedoc --plugin typedoc-plugin-markdown --out docs src/index.ts"
|
|
13
41
|
}
|
|
14
|
-
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: assert
|
|
3
|
+
description: >-
|
|
4
|
+
Type-safe assertion and validation utilities for defensive TypeScript programming.
|
|
5
|
+
Use when writing runtime assertions, null checks, type guard validations, unreachable code markers, or exhaustive switch/if-else checking.
|
|
6
|
+
Import named functions from @rightcapital/assert.
|
|
7
|
+
license: MIT
|
|
8
|
+
metadata:
|
|
9
|
+
author: RightCapital
|
|
10
|
+
package: '@rightcapital/assert'
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# assert
|
|
14
|
+
|
|
15
|
+
Type-safe assertion utilities for defensive TypeScript programming. All functions throw `AssertError` (which extends `Error`) on failure. Default error message format is `${functionName}: Unexpected ${String(value)}`.
|
|
16
|
+
|
|
17
|
+
## Import
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import {
|
|
21
|
+
assert,
|
|
22
|
+
AssertError,
|
|
23
|
+
assertExhaustive,
|
|
24
|
+
assertNonNullable,
|
|
25
|
+
assertUnreachable,
|
|
26
|
+
ensure,
|
|
27
|
+
ensureNonNullable,
|
|
28
|
+
} from '@rightcapital/assert';
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Reference
|
|
32
|
+
|
|
33
|
+
| Class / Function | Signature / Return | Use Case |
|
|
34
|
+
| -------------------------------- | --------------------------------- | ------------------------------------------------------------------- |
|
|
35
|
+
| `AssertError` | `class extends Error` | Error type thrown when any assertion in this package fails. |
|
|
36
|
+
| `assert(value, msg?)` | `asserts value` | Precondition or boolean check (verifies `value === true`). |
|
|
37
|
+
| `assertNonNullable(value, msg?)` | `asserts value is NonNullable<T>` | Guard statement against `null` or `undefined`. |
|
|
38
|
+
| `ensure(value, predicate, msg?)` | `S extends T` | Inline validation using type guard function `(val: T) => val is S`. |
|
|
39
|
+
| `ensureNonNullable(value, msg?)` | `NonNullable<T>` | Inline assignment or method chain for non-null value. |
|
|
40
|
+
| `assertExhaustive(value, msg?)` | `value: never` -> `never` | Exhaustiveness check in `switch` `default` case or `if-else` chain. |
|
|
41
|
+
| `assertUnreachable(msg?)` | `never` | Mark logically impossible code paths. |
|
|
42
|
+
|
|
43
|
+
## Error Handling with `AssertError`
|
|
44
|
+
|
|
45
|
+
All assertion functions throw `AssertError` when a condition is not met. Use `instanceof AssertError` to catch assertion failures specifically:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { assert, AssertError } from '@rightcapital/assert';
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
assert(age >= 18, 'User must be an adult');
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (error instanceof AssertError) {
|
|
54
|
+
console.error('Assertion failed:', error.message);
|
|
55
|
+
} else {
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Critical Rules for Code Generation
|
|
62
|
+
|
|
63
|
+
### 1. `assert()` Checks Strict Boolean Equality (`value !== true`)
|
|
64
|
+
|
|
65
|
+
`assert(value)` throws if `value !== true`. It throws for truthy non-boolean values like objects or strings.
|
|
66
|
+
|
|
67
|
+
- **Incorrect**: `assert(user);` (throws even if `user` is an object)
|
|
68
|
+
- **Correct**: `assertNonNullable(user);`
|
|
69
|
+
- **Correct**: `assert(user.age >= 18, 'Must be 18+');`
|
|
70
|
+
|
|
71
|
+
### 2. Choose `assert*` Statements vs `ensure*` Expressions
|
|
72
|
+
|
|
73
|
+
- **`assert*` functions** are statements. They narrow the type of an existing variable for subsequent code.
|
|
74
|
+
- **`ensure*` functions** are expressions. They return the validated value with a narrowed type for direct assignment or chaining.
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
// Statement form
|
|
78
|
+
assertNonNullable(user);
|
|
79
|
+
console.log(user.name);
|
|
80
|
+
|
|
81
|
+
// Expression form
|
|
82
|
+
const userName = ensureNonNullable(getUser()).name;
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 3. `ensure()` Requires a Type Guard Predicate
|
|
86
|
+
|
|
87
|
+
The `predicate` parameter in `ensure(val, predicate)` must be a TypeScript type guard (`(val: T) => val is S`). A standard boolean function without a type predicate will cause a compile error.
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
function isAdminUser(user: User): user is AdminUser {
|
|
91
|
+
return user.role === 'admin';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const admin = ensure(currentUser, isAdminUser, 'Admin required');
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 4. `assertExhaustive` vs `assertUnreachable`
|
|
98
|
+
|
|
99
|
+
- Use `assertExhaustive(value)` when TypeScript can prove all union cases are handled (e.g., `switch` default case). It catches missing union members at compile time because `value` must be typed as `never`.
|
|
100
|
+
- Use `assertUnreachable(message)` for defensive checks in branches that TypeScript cannot prove unreachable.
|
|
101
|
+
- Prefix with `return` in non-void functions (for example, `return assertExhaustive(val);` or `return assertUnreachable(msg);`) to satisfy ESLint `consistent-return`.
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// Exhaustive switch over union
|
|
105
|
+
switch (shape.kind) {
|
|
106
|
+
case 'circle':
|
|
107
|
+
return Math.PI * shape.radius ** 2;
|
|
108
|
+
case 'square':
|
|
109
|
+
return shape.side ** 2;
|
|
110
|
+
default:
|
|
111
|
+
return assertExhaustive(shape);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Unreachable defensive branch
|
|
115
|
+
if (status === 'active') {
|
|
116
|
+
handleActive();
|
|
117
|
+
} else if (status === 'inactive') {
|
|
118
|
+
handleInactive();
|
|
119
|
+
} else {
|
|
120
|
+
return assertUnreachable(`Unexpected status: ${status}`);
|
|
121
|
+
}
|
|
122
|
+
```
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assertion functions provide type-safe assertions and validation.
|
|
3
|
+
* When assertions fail, they throw `AssertError`.
|
|
4
|
+
*
|
|
5
|
+
* @author lixiaoyan <lxy.lixiaoyan@gmail.com>
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* // Basic assertion
|
|
9
|
+
* assert(user.age >= 18, 'User must be at least 18 years old');
|
|
10
|
+
*
|
|
11
|
+
* // Non-nullable assertion with type narrowing
|
|
12
|
+
* assertNonNullable(user, 'User cannot be null');
|
|
13
|
+
* // user is now typed as NonNullable<T>
|
|
14
|
+
*
|
|
15
|
+
* // Ensure with type guard
|
|
16
|
+
* const admin = ensure(currentUser, isAdminUser, 'Admin required');
|
|
17
|
+
* // admin is now typed as AdminUser
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Error thrown when an assertion fails.
|
|
23
|
+
*/
|
|
24
|
+
export class AssertError extends Error {
|
|
25
|
+
public override readonly name = 'AssertError';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Throws an AssertError for failed assertions.
|
|
30
|
+
*/
|
|
31
|
+
function throwError(
|
|
32
|
+
name: string,
|
|
33
|
+
value: unknown,
|
|
34
|
+
message: string | undefined,
|
|
35
|
+
): never {
|
|
36
|
+
throw new AssertError(message ?? `${name}: Unexpected ${String(value)}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Basic assertion: verifies that a value or expression is `true`, otherwise throws an exception.
|
|
41
|
+
*
|
|
42
|
+
* @param value - The value to assert as truthy
|
|
43
|
+
* @param message - Optional custom error message
|
|
44
|
+
* @throws {AssertError} Throws an error if `value` is not `true`.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* // Basic usage
|
|
49
|
+
* assert(user.age >= 18, 'User must be at least 18 years old');
|
|
50
|
+
*
|
|
51
|
+
* // Condition validation
|
|
52
|
+
* const isValid = validateData(data);
|
|
53
|
+
* assert(isValid, 'Data validation failed');
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export function assert(value: unknown, message?: string): asserts value {
|
|
57
|
+
if (value !== true) {
|
|
58
|
+
throwError('assert', value, message);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Asserts that a value is not `null` or `undefined`, providing TypeScript type narrowing to `NonNullable<T>`.
|
|
64
|
+
* Ensures subsequent code can safely access the value.
|
|
65
|
+
*
|
|
66
|
+
* @param value - The value to check for null/undefined
|
|
67
|
+
* @param message - Optional custom error message
|
|
68
|
+
* @throws {AssertError} Throws an error if `value` is `null` or `undefined`.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* function processUser(user: User | null | undefined) {
|
|
73
|
+
* assertNonNullable(user, 'User cannot be null');
|
|
74
|
+
* // user is now typed as User
|
|
75
|
+
* console.log(user.name); // Safe to access
|
|
76
|
+
* }
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export function assertNonNullable<T>(
|
|
80
|
+
value: T,
|
|
81
|
+
message?: string,
|
|
82
|
+
): asserts value is NonNullable<T> {
|
|
83
|
+
if (value === null || value === undefined) {
|
|
84
|
+
throwError('assertNonNullable', value, message);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Similar to `assert`, but returns the value. Ensures a value matches a type predicate and returns it with narrowed type.
|
|
90
|
+
*
|
|
91
|
+
* @param value - The value to validate
|
|
92
|
+
* @param predicate - Type guard function that validates the value
|
|
93
|
+
* @param message - Optional custom error message
|
|
94
|
+
* @returns The value with narrowed type
|
|
95
|
+
* @throws {AssertError} Throws an error if `predicate` returns `false`.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```typescript
|
|
99
|
+
* // Define type guard
|
|
100
|
+
* function isAdminUser(user: User): user is AdminUser {
|
|
101
|
+
* return user.role === 'admin';
|
|
102
|
+
* }
|
|
103
|
+
*
|
|
104
|
+
* // Use ensure to get type-safe value
|
|
105
|
+
* const admin = ensure(
|
|
106
|
+
* currentUser,
|
|
107
|
+
* isAdminUser,
|
|
108
|
+
* 'Admin privileges required'
|
|
109
|
+
* );
|
|
110
|
+
* // admin is typed as AdminUser
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export function ensure<T, S extends T>(
|
|
114
|
+
value: T,
|
|
115
|
+
predicate: (value: T) => value is S,
|
|
116
|
+
message?: string,
|
|
117
|
+
): S {
|
|
118
|
+
assert(predicate(value), message);
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Similar to `assertNonNullable`, but returns the value. Ensures a value is not null/undefined and returns it.
|
|
124
|
+
*
|
|
125
|
+
* @param value - The value to check for null/undefined
|
|
126
|
+
* @param message - Optional custom error message
|
|
127
|
+
* @returns The non-nullable value
|
|
128
|
+
* @throws {AssertError} Throws an error if `value` is `null` or `undefined`.
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```typescript
|
|
132
|
+
* // Use in expressions
|
|
133
|
+
* const config = ensureNonNullable(
|
|
134
|
+
* getConfig(),
|
|
135
|
+
* 'Configuration not found'
|
|
136
|
+
* );
|
|
137
|
+
*
|
|
138
|
+
* // Chain calls
|
|
139
|
+
* const userName = ensureNonNullable(user, 'User not found').name;
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
export function ensureNonNullable<T>(
|
|
143
|
+
value: T,
|
|
144
|
+
message?: string,
|
|
145
|
+
): NonNullable<T> {
|
|
146
|
+
if (value === null || value === undefined) {
|
|
147
|
+
throwError('ensureNonNullable', value, message);
|
|
148
|
+
}
|
|
149
|
+
return value;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Marks code branches that should theoretically never be reached.
|
|
154
|
+
* Used for defensive programming to prevent unexpected code execution when data or logic doesn't match expectations.
|
|
155
|
+
*
|
|
156
|
+
* @param message - Optional custom error message
|
|
157
|
+
* @returns Never returns (always throws)
|
|
158
|
+
* @throws {AssertError} Always thrown.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```typescript
|
|
162
|
+
* function processStatus(status: 'active' | 'inactive' | 'activating') {
|
|
163
|
+
* if (status === 'active') {
|
|
164
|
+
* // Handle active state
|
|
165
|
+
* } else if (status === 'inactive') {
|
|
166
|
+
* // Handle inactive state
|
|
167
|
+
* } else {
|
|
168
|
+
* // According to business logic, this should never be reached
|
|
169
|
+
* assertUnreachable(`Unexpected status value: ${status}`);
|
|
170
|
+
* }
|
|
171
|
+
* }
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
export function assertUnreachable(message?: string): never {
|
|
175
|
+
throwError('assertUnreachable', null, message);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Used for exhaustiveness checking of union types. Ensures switch or if-else statements cover all possible types.
|
|
180
|
+
* Leverages TypeScript's `never` type to catch missing branches at compile time.
|
|
181
|
+
*
|
|
182
|
+
* @param value - The value that should be `never` if all cases are handled
|
|
183
|
+
* @param message - Optional custom error message
|
|
184
|
+
* @returns Never returns (always throws)
|
|
185
|
+
* @throws {AssertError} Always thrown.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* type Action =
|
|
190
|
+
* | { type: 'ADD'; payload: number }
|
|
191
|
+
* | { type: 'SUBTRACT'; payload: number }
|
|
192
|
+
* | { type: 'MULTIPLY'; payload: number };
|
|
193
|
+
*
|
|
194
|
+
* function reducer(action: Action) {
|
|
195
|
+
* switch (action.type) {
|
|
196
|
+
* case 'ADD':
|
|
197
|
+
* return state + action.payload;
|
|
198
|
+
* case 'SUBTRACT':
|
|
199
|
+
* return state - action.payload;
|
|
200
|
+
* case 'MULTIPLY':
|
|
201
|
+
* return state * action.payload;
|
|
202
|
+
* default:
|
|
203
|
+
* // If all types are exhausted, action is of type never
|
|
204
|
+
* // If new Action types are added but not handled, TypeScript will report error
|
|
205
|
+
* return assertExhaustive(action);
|
|
206
|
+
* // ^ never
|
|
207
|
+
* }
|
|
208
|
+
* }
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
export function assertExhaustive(value: never, message?: string): never {
|
|
212
|
+
throwError('assertExhaustive', value, message);
|
|
213
|
+
}
|