A small library for working with 0- and 1-based offsets in a type-checked way.
npx @tessl/cli install tessl/npm-ob1@0.83.00
# ob1
1
2
ob1 is a small TypeScript library that provides type-safe operations for working with 0-based and 1-based numeric offsets using Flow's opaque types. It prevents common off-by-one errors by enforcing type distinctions at compile time while maintaining runtime efficiency through simple numeric operations.
3
4
## Package Information
5
6
- **Package Name**: ob1
7
- **Package Type**: npm
8
- **Language**: JavaScript with Flow types
9
- **Installation**: `npm install ob1`
10
11
## Core Imports
12
13
```javascript
14
const { add, sub, get0, get1, add1, sub1, neg, add0, inc } = require("ob1");
15
```
16
17
ES Modules:
18
```javascript
19
import { add, sub, get0, get1, add1, sub1, neg, add0, inc } from "ob1";
20
```
21
22
Flow Types:
23
```javascript
24
import type { Number0, Number1 } from "ob1";
25
```
26
27
## Basic Usage
28
29
```javascript
30
const { add0, add1, add, get0, get1 } = require("ob1");
31
32
// Create typed offsets
33
const zeroBasedIndex = add0(42); // Number0 type
34
const oneBasedLine = add1(41); // Number1 type (42 in 1-based)
35
36
// Type-safe arithmetic
37
const sum = add(zeroBasedIndex, 3); // Number0 + number = Number0
38
const mixed = add(zeroBasedIndex, oneBasedLine); // Number0 + Number1 = Number1
39
40
// Extract values
41
console.log(get0(sum)); // 45
42
console.log(get1(mixed)); // 85
43
```
44
45
## Architecture
46
47
ob1's architecture is built around Flow's opaque type system:
48
49
- **Opaque Types**: `Number0` and `Number1` are distinct numeric types that prevent accidental mixing
50
- **Type Safety**: Operations preserve offset semantics through overloaded function signatures
51
- **Runtime Efficiency**: All operations are simple numeric calculations with zero runtime overhead
52
- **Compile-time Validation**: Flow prevents unsafe operations like adding two 1-based offsets
53
54
## Capabilities
55
56
### Type System
57
58
Core opaque types for representing different offset bases.
59
60
```javascript { .api }
61
// 0-based offset type (for array indices, positions starting from 0)
62
export opaque type Number0 = number;
63
64
// 1-based offset type (for line numbers, columns starting from 1)
65
export opaque type Number1 = number;
66
```
67
68
### Arithmetic Operations
69
70
Type-safe addition and subtraction with automatic type propagation.
71
72
```javascript { .api }
73
/**
74
* Add two offsets or numbers with type-safe result types
75
* @param a - First operand (Number0, Number1, or number)
76
* @param b - Second operand (Number0, Number1, or number)
77
* @returns Result preserving appropriate offset type
78
*/
79
declare function add(a: Number1, b: number): Number1;
80
declare function add(a: number, b: Number1): Number1;
81
declare function add(a: Number0, b: number): Number0;
82
declare function add(a: number, b: Number0): Number0;
83
declare function add(a: Number1, b: Number0): Number1;
84
declare function add(a: Number0, b: Number1): Number1;
85
declare function add(a: Number0, b: Number0): Number0;
86
87
/**
88
* Subtract a number or 0-based offset from a 1/0-based offset
89
* @param a - Minuend (Number0, Number1, or number)
90
* @param b - Subtrahend (Number0, Number1, or number)
91
* @returns Result with appropriate offset type
92
*/
93
declare function sub(a: Number1, b: number): Number1;
94
declare function sub(a: Number0, b: number): Number0;
95
declare function sub(a: number, b: Number0): Number0;
96
declare function sub(a: Number0, b: number): Number0;
97
declare function sub(a: Number1, b: Number0): Number1;
98
declare function sub(a: Number0, b: Number0): Number0;
99
declare function sub(a: Number1, b: Number1): Number0;
100
```
101
102
**Usage Examples:**
103
104
```javascript
105
const { add0, add1, add, sub, get0, get1 } = require("ob1");
106
107
const index = add0(5); // Number0
108
const line = add1(10); // Number1
109
110
// Safe additions
111
const newIndex = add(index, 3); // Number0 + number = Number0
112
const newLine = add(line, index); // Number1 + Number0 = Number1
113
114
// Safe subtractions
115
const diff = sub(line, line); // Number1 - Number1 = Number0 (distance)
116
const prevIndex = sub(index, 2); // Number0 - number = Number0
117
118
console.log(get0(newIndex)); // 8
119
console.log(get1(newLine)); // 16
120
console.log(get0(diff)); // 0
121
```
122
123
### Type Casting and Extraction
124
125
Functions for creating offset types and extracting underlying values.
126
127
```javascript { .api }
128
/**
129
* Cast a number to a 0-based offset
130
* @param x - Number to cast
131
* @returns Number0 offset type
132
*/
133
function add0(x: number): Number0;
134
135
/**
136
* Get the underlying number of a 0-based offset, casting away the opaque type
137
* @param x - Number0 offset or null/undefined
138
* @returns Underlying number value or null/undefined
139
*/
140
declare function get0(x: Number0): number;
141
declare function get0(x: void | null): void | null;
142
143
/**
144
* Get the underlying number of a 1-based offset, casting away the opaque type
145
* @param x - Number1 offset or null/undefined
146
* @returns Underlying number value or null/undefined
147
*/
148
declare function get1(x: Number1): number;
149
declare function get1(x: void | null): void | null;
150
```
151
152
### Offset Conversion
153
154
Functions for converting between 0-based and 1-based offset types.
155
156
```javascript { .api }
157
/**
158
* Add 1 to a 0-based offset, converting it to 1-based
159
* @param x - Number0 offset or regular number
160
* @returns Number1 offset (input + 1)
161
*/
162
function add1(x: Number0 | number): Number1;
163
164
/**
165
* Subtract 1 from a 1-based offset, converting it to 0-based
166
* @param x - Number1 offset
167
* @returns Number0 offset (input - 1)
168
*/
169
function sub1(x: Number1): Number0;
170
```
171
172
**Usage Examples:**
173
174
```javascript
175
const { add0, add1, sub1, get0, get1 } = require("ob1");
176
177
// Array index to line number conversion
178
const arrayIndex = add0(4); // 5th element (0-based)
179
const lineNumber = add1(arrayIndex); // Line 5 (1-based)
180
181
// Line number to array index conversion
182
const currentLine = add1(42); // Line 43 (1-based)
183
const arrayPos = sub1(currentLine); // Index 42 (0-based)
184
185
console.log(get0(arrayIndex)); // 4
186
console.log(get1(lineNumber)); // 5
187
console.log(get0(arrayPos)); // 42
188
```
189
190
### Utility Operations
191
192
Additional operations for incrementing and negating offsets.
193
194
```javascript { .api }
195
/**
196
* Increment a 0-based or 1-based offset by 1
197
* @param a - Offset to increment (Number0 or Number1)
198
* @returns Same offset type incremented by 1
199
*/
200
declare function inc(a: Number0): Number0;
201
declare function inc(a: Number1): Number1;
202
203
/**
204
* Negate a 0-based offset
205
* @param x - Number0 offset to negate
206
* @returns Negated Number0 offset
207
*/
208
function neg(x: Number0): Number0;
209
```
210
211
**Usage Examples:**
212
213
```javascript
214
const { add0, add1, inc, neg, get0, get1 } = require("ob1");
215
216
const index = add0(5);
217
const line = add1(10);
218
219
// Increment operations preserve type
220
const nextIndex = inc(index); // Number0: 6
221
const nextLine = inc(line); // Number1: 11
222
223
// Negation (only for Number0)
224
const negative = neg(index); // Number0: -5
225
226
console.log(get0(nextIndex)); // 6
227
console.log(get1(nextLine)); // 11
228
console.log(get0(negative)); // -5
229
```
230
231
## Type Safety Features
232
233
### Allowed Operations
234
235
ob1's type system allows these safe combinations:
236
237
- `Number0 + Number0 = Number0` - Adding two 0-based offsets
238
- `Number0 + Number1 = Number1` - Adding 0-based to 1-based yields 1-based
239
- `Number1 + Number0 = Number1` - Adding 1-based to 0-based yields 1-based
240
- `Number1 - Number1 = Number0` - Distance between 1-based positions
241
- `Number0/Number1 + number` - Adding plain numbers preserves offset type
242
243
### Prevented Operations
244
245
Flow will prevent these unsafe operations at compile time:
246
247
- `Number1 + Number1` - Adding two 1-based offsets is mathematically meaningless
248
- `Number0 - Number1` - Would create negative 0-based offsets
249
- `get0(Number1)` - Cross-type extraction
250
- `get1(Number0)` - Cross-type extraction
251
- `add1(Number1)` - Converting already 1-based offset
252
- `sub1(Number0)` - Converting already 0-based offset
253
- Direct arithmetic (`offset + 1`, `offset - 1`) - Must use provided functions
254
255
## Error Handling
256
257
ob1 operations are purely mathematical and do not throw exceptions. Invalid combinations are prevented at compile time through Flow's type system rather than runtime checks.
258
259
For null/undefined handling, the `get0` and `get1` functions have overloaded signatures that preserve null/undefined values:
260
261
```javascript
262
get0(null); // returns null
263
get0(undefined); // returns undefined
264
get1(null); // returns null
265
get1(undefined); // returns undefined
266
```