Skip to content

Commit 310bdff

Browse files
authored
Merge pull request #14 from lambdalisue/v2
💥 Breaking API changes (v2)
2 parents 4d7b507 + ba7cf96 commit 310bdff

File tree

12 files changed

+1105
-724
lines changed

12 files changed

+1105
-724
lines changed

README.md

Lines changed: 110 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@ The `unknownutil` provides the following predicate functions
2424
- `isFunction(x: unknown): x is (...args: unknown[]) => unknown`
2525
- `isNull(x: unknown): x is null`
2626
- `isUndefined(x: unknown): x is undefined`
27-
- `isNone(x: unknown): x is null | undefined`
27+
- `isNullish(x: unknown): x is null | undefined`
2828
- `isLike<R, T extends unknown>(ref: R, x: unknown, pred?: Predicate<T>): x is R`
2929

30+
The above function can be used to check the type of any variable and guarantee
31+
its type inside a closed `if` scope.
32+
3033
For example:
3134

3235
```typescript
@@ -56,7 +59,7 @@ if (isArray(a, isString)) {
5659
}
5760
```
5861

59-
Use `isLike` if you need some complicated types like tuple or struct like:
62+
Use `isLike` if you need some complicated types like:
6063

6164
```typescript
6265
import { isLike } from "https://deno.land/x/unknownutil/mod.ts";
@@ -89,119 +92,148 @@ if (isLike({ foo: "", bar: 0 }, e)) {
8992
}
9093
```
9194

92-
### ensureXXXXX
95+
### assertXXXXX
96+
97+
The `unknownutil` provides the following assert functions
9398

94-
The `unknownutil` provides the following ensure functions which will raise
95-
`EnsureError` when a given `x` is not expected type.
99+
- `assertString(x: unknown): assert x is string`
100+
- `assertNumber(x: unknown): assert x is number`
101+
- `assertBoolean(x: unknown): assert x is boolean`
102+
- `assertArray<T extends unknown>(x: unknown, pred?: Predicate<T>): assert x is T[]`
103+
- `assertObject<T extends unknown>(x: unknown, pred?: Predicate<T>): assert x is Record<string, T>`
104+
- `assertFunction(x: unknown): assert x is (...args: unknown[]) => unknown`
105+
- `assertNull(x: unknown): assert x is null`
106+
- `assertUndefined(x: unknown): assert x is undefined`
107+
- `assertNullish(x: unknown): assert x is null | undefined`
108+
- `assertLike<R, T extends unknown>(ref: R, x: unknown, pred?: Predicate<T>): assert x is R`
96109

97-
- `ensure<T>(x: unknown, pred: Predicate<T>, message?: string): assert x is T`
98-
- `ensureString(x: unknown): assert x is string`
99-
- `ensureNumber(x: unknown): assert x is number`
100-
- `ensureBoolean(x: unknown): assert x is boolean`
101-
- `ensureArray<T extends unknown>(x: unknown, pred?: Predicate<T>): assert x is T[]`
102-
- `ensureObject<T extends unknown>(x: unknown, pred?: Predicate<T>): x is Record<string, T>`
103-
- `ensureFunction(x: unknown): x is (...args: unknown[]) => unknown`
104-
- `ensureNull(x: unknown): x is null`
105-
- `ensureUndefined(x: unknown): x is undefined`
106-
- `ensureNone(x: unknown): x is null | undefined`
110+
The above function can be used to guarantee the type of any variable by throwing
111+
an exception if the type is not expected.
107112

108113
For example:
109114

110115
```typescript
111-
import { ensureString } from "https://deno.land/x/unknownutil/mod.ts";
116+
import { assertString } from "https://deno.land/x/unknownutil/mod.ts";
112117

113-
const a: unknown = "Hello";
114-
ensureString(a); // Now 'a' is 'string'
118+
function say(message: string): void {
119+
console.log(message);
120+
}
115121

122+
const a: unknown = "Hello";
116123
const b: unknown = 0;
117-
ensureString(b); // Raise EnsureError on above while 'b' is not string
124+
125+
// Because 'a' is 'unknown', TypeScript won't allow a code like below
126+
//say(a);
127+
128+
// But once the 'assertString(a)' is passed, TypeScript knows that 'a' is 'string'
129+
// thus it accepts the code that was not accepted before.
130+
assertString(a);
131+
say(a);
132+
133+
// Or raise 'AssertError' if a given value is not string
134+
assertString(b);
135+
say(b);
118136
```
119137

120-
Additionally, `ensureArray` and `ensureObject` supports an inner predicate
121-
function to predicate `x` more precisely like:
138+
More complex type predications are available on `assertXXXXX` as well like
139+
`isXXXXX`.
122140

123-
```typescript
124-
import { ensureArray, isString } from "https://deno.land/x/unknownutil/mod.ts";
141+
### ensureXXXXX
125142

126-
const a: unknown = ["a", "b", "c"];
127-
ensureArray(a); // Now 'a' is 'unknown[]'
128-
ensureArray(a, isString); // Now 'a' is 'string[]'
143+
The `unknownutil` provides the following ensure functions
129144

130-
const b: unknown = [0, 1, 2];
131-
ensureArray(b); // Now 'b' is 'unknown[]'
132-
ensureArray(b, isString); // Raise EnsureError on above while 'b' is not string array
133-
```
145+
- `ensureString(x: unknown): string`
146+
- `ensureNumber(x: unknown): number`
147+
- `ensureBoolean(x: unknown): boolean`
148+
- `ensureArray<T extends unknown>(x: unknown, pred?: Predicate<T>): T[]`
149+
- `ensureObject<T extends unknown>(x: unknown, pred?: Predicate<T>): Record<string, T>`
150+
- `ensureFunction(x: unknown): (...args: unknown[]) => unknown`
151+
- `ensureNull(x: unknown): null`
152+
- `ensureUndefined(x: unknown): undefined`
153+
- `ensureNullish(x: unknown): null | undefined`
154+
- `ensureLike<R, T extends unknown>(ref: R, x: unknown, pred?: Predicate<T>): R`
134155

135-
Use `ensureLike` if you need some complicated types like tuple or struct like:
156+
The above function can be used to guarantee the type of any variable by throwing
157+
an exception if the type is not expected. The difference between assert and
158+
ensure is whether to assert the argument or the return type.
159+
160+
For example:
136161

137162
```typescript
138-
import { ensureLike } from "https://deno.land/x/unknownutil/mod.ts";
163+
import { ensureString } from "https://deno.land/x/unknownutil/mod.ts";
139164

140-
const a: unknown = ["a", "b", "c"];
141-
ensureLike([], a); // Now 'a' is 'unknown[]'
142-
ensureLike(["", "", ""], a); // Now 'a' is '[string, string, string]'
165+
function say(message: string): void {
166+
console.log(message);
167+
}
143168

144-
const b: unknown = { foo: "foo", bar: 0 };
145-
ensureLike({}, b); // Now 'b' is 'Record<string, unknown>'
146-
ensureLike({ foo: "", bar: 0 }, b); // Now 'b' is '{foo: string, bar: number}'
169+
const a: unknown = "Hello";
170+
const b: unknown = 0;
171+
172+
// Because 'a' is 'unknown', TypeScript won't allow a code like below
173+
//say(a);
174+
175+
// But once the 'ensureString(a)' is passed, TypeScript knows that return value is 'string'
176+
// thus it accepts the code.
177+
say(ensureString(a));
178+
179+
// Or raise 'AssertError' if a given value is not string
180+
say(ensureString(b));
147181
```
148182

149-
### assumeXXXXX
183+
More complex type predications are available on `ensureXXXXX` as well like
184+
`isXXXXX`.
150185

151-
The `unknownutil` provides the following assume functions which returns a given
152-
`x` as is or raise `EnsureError` when that is not expected type.
186+
### maybeXXXXX
153187

154-
- `assume<T>(x: unknown, pred: Predicate<T>, message?: string): T`
155-
- `assumeString(x: unknown): string`
156-
- `assumeNumber(x: unknown): number`
157-
- `assumeBoolean(x: unknown): boolean`
158-
- `assumeArray<T extends unknown>(x: unknown, pred?: Predicate<T>): T[]`
159-
- `assumeObject<T extends unknown>(x: unknown, pred?: Predicate<T>): Record<string, T>`
160-
- `assumeFunction(x: unknown): (...args: unknown[]) => unknown`
161-
- `assumeNull(x: unknown): null`
162-
- `assumeUndefined(x: unknown): undefined`
163-
- `assumeNone(x: unknown): null | undefined`
188+
The `unknownutil` provides the following maybe functions
189+
190+
- `maybeString(x: unknown): string | undefined`
191+
- `maybeNumber(x: unknown): number | undefined`
192+
- `maybeBoolean(x: unknown): boolean | undefined`
193+
- `maybeArray<T extends unknown>(x: unknown, pred?: Predicate<T>): T[] | undefined`
194+
- `maybeObject<T extends unknown>(x: unknown, pred?: Predicate<T>): Record<string, T> | undefined`
195+
- `maybeFunction(x: unknown): ((...args: unknown[]) => unknown) | undefined`
196+
- `maybeLike<R, T extends unknown>(ref: R, x: unknown, pred?: Predicate<T>): R | undefined`
197+
198+
The above function will return `undefined` if the type of any variable is not
199+
expected, so it is possible to give an alternative value using the Nullish
200+
coalescing operator (`??`).
164201

165202
For example:
166203

167204
```typescript
168-
import { assumeString } from "https://deno.land/x/unknownutil/mod.ts";
205+
import { maybeString } from "https://deno.land/x/unknownutil/mod.ts";
169206

170-
const a: unknown = "Hello";
171-
const a1 = assumeString(a); // Now 'a' and 'a1' is 'string'
207+
function say(message: string): void {
208+
console.log(message);
209+
}
172210

211+
const a: unknown = "Hello";
173212
const b: unknown = 0;
174-
const b1 = assumeString(b); // Raise EnsureError on above while 'b' is not string
175-
```
176213

177-
Additionally, `assumeArray` and `assumeObject` supports an inner predicate
178-
function to predicate `x` more precisely like:
214+
// Because 'a' is 'unknown', TypeScript won't allow a code like below
215+
//say(a);
179216

180-
```typescript
181-
import { assumeArray, isString } from "https://deno.land/x/unknownutil/mod.ts";
182-
183-
const a: unknown = ["a", "b", "c"];
184-
const a1 = assumeArray(a); // Now 'a' and 'a1' is 'unknown[]'
185-
const a2 = assumeArray(a, isString); // Now 'a' and 'a2' is 'string[]'
217+
// But the 'maybeString(a)' returns 'string | undefined' thus users can use
218+
// Nullish coalescing operator to give an alternative value to ensure that the
219+
// value given to the 'say()' is 'string'.
220+
// The following code print "Hello" to the console.
221+
say(maybeString(a) ?? "World");
186222

187-
const b: unknown = [0, 1, 2];
188-
const b1 = assumeArray(b); // Now 'b' and 'b1' is 'unknown[]'
189-
const b2 = assumeArray(b, isString); // Raise EnsureError on above while 'b' is not string array
223+
// The following code print "World" to the console.
224+
say(maybeString(b) ?? "World");
190225
```
191226

192-
Use `assumeLike` if you need some complicated types like tuple or struct like:
227+
More complex type predications are available on `maybeXXXXX` as well like
228+
`isXXXXX`.
193229

194-
```typescript
195-
import { assumeLike } from "https://deno.land/x/unknownutil/mod.ts";
196-
197-
const a: unknown = ["a", "b", "c"];
198-
const a1 = assumeLike([], a); // Now 'a' and 'a1' is 'unknown[]'
199-
const a2 = assumeLike(["", "", ""], a); // Now 'a' and 'a2' is '[string, string, string]'
230+
## Migration from v1 to v2
200231

201-
const b: unknown = { foo: "foo", bar: 0 };
202-
const b1 = assumeLike({}, b); // Now 'b' and 'b1' is 'Record<string, unknown>'
203-
const b2 = assumeLike({ foo: "", bar: 0 }, b); // Now 'b' and 'b2' is '{foo: string, bar: number}'
204-
```
232+
1. Replace `ensure` or `assert` to corresponding specific functions (e.g.
233+
`ensureString` or `assertNumber`)
234+
2. Rename `xxxxxNone` to `xxxxxNullish` (e.g. `isNone` to `isNullish`)
235+
3. Rename `ensureXXXXX` to `assertXXXXX` (e.g. `ensureString` to `assertString`)
236+
4. Rename `assumeXXXXX` to `ensureXXXXX` (e.g. `assumeNumber` to `ensureNumber`)
205237

206238
## Development
207239

assert.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import {
2+
isArray,
3+
isBoolean,
4+
isFunction,
5+
isLike,
6+
isNull,
7+
isNullish,
8+
isNumber,
9+
isObject,
10+
isString,
11+
isUndefined,
12+
Predicate,
13+
} from "./is.ts";
14+
15+
/**
16+
* An error raised by type assertion functions
17+
*/
18+
export class AssertError extends Error {
19+
constructor(message?: string) {
20+
super(message);
21+
22+
if (Error.captureStackTrace) {
23+
Error.captureStackTrace(this, AssertError);
24+
}
25+
26+
this.name = "AssertError";
27+
}
28+
}
29+
30+
function assert<T>(
31+
x: unknown,
32+
pred: Predicate<T>,
33+
message = "The value is not expected type",
34+
): asserts x is T {
35+
if (!pred(x)) {
36+
throw new AssertError(message);
37+
}
38+
}
39+
40+
/**
41+
* Ensure that `x` is string, and raise `AssertError` if it is not.
42+
*/
43+
export function assertString(x: unknown): asserts x is string {
44+
return assert(x, isString, "The value must be string");
45+
}
46+
47+
/**
48+
* Ensure that `x` is number, and raise `AssertError` if it is not.
49+
*/
50+
export function assertNumber(x: unknown): asserts x is number {
51+
return assert(x, isNumber, "The value must be number");
52+
}
53+
54+
/**
55+
* Ensure that `x` is boolean, and raise `AssertError` if it is not.
56+
*/
57+
export function assertBoolean(x: unknown): asserts x is boolean {
58+
return assert(x, isBoolean, "The value must be boolean");
59+
}
60+
61+
/**
62+
* Ensure that `x` is array, and raise `AssertError` if it is not.
63+
*
64+
* Use `ipred` to predicate the type of items.
65+
*/
66+
export function assertArray<T extends unknown>(
67+
x: unknown,
68+
ipred?: Predicate<T>,
69+
): asserts x is T[] {
70+
const pred = (x: unknown): x is T[] => isArray(x, ipred);
71+
return assert(x, pred, "The value must be array");
72+
}
73+
74+
/**
75+
* Ensure that `x` is object, and raise `AssertError` if it is not.
76+
*
77+
* Use `ipred` to predicate the type of values.
78+
*/
79+
export function assertObject<T>(
80+
x: unknown,
81+
ipred?: Predicate<T>,
82+
): asserts x is Record<string, T> {
83+
const pred = (x: unknown): x is Record<string, T> => isObject(x, ipred);
84+
return assert(x, pred, "The value must be object");
85+
}
86+
87+
/**
88+
* Ensure that `x` is function, and raise `AssertError` if it is not.
89+
*/
90+
export function assertFunction(
91+
x: unknown,
92+
): asserts x is (...args: unknown[]) => unknown {
93+
return assert(x, isFunction, "The value must be function");
94+
}
95+
96+
/**
97+
* Ensure that `x` is null, and raise `AssertError` if it is not.
98+
*/
99+
export function assertNull(x: unknown): asserts x is null {
100+
return assert(x, isNull, "The value must be null");
101+
}
102+
103+
/**
104+
* Ensure that `x` is undefined, and raise `AssertError` if it is not.
105+
*/
106+
export function assertUndefined(x: unknown): asserts x is undefined {
107+
return assert(x, isUndefined, "The value must be undefined");
108+
}
109+
110+
/**
111+
* Ensure that `x` is null or undefined, and raise `AssertError` if it is not.
112+
*/
113+
export function assertNullish(x: unknown): asserts x is null | undefined {
114+
return assert(x, isNullish, "The value must be null or undefined");
115+
}
116+
117+
/**
118+
* Ensure that `x` follows the type of `ref`, and raise `AssertError` if it is not.
119+
*/
120+
export function assertLike<R, T extends unknown>(
121+
ref: R,
122+
x: unknown,
123+
ipred?: Predicate<T>,
124+
): asserts x is R {
125+
const pred = (x: unknown): x is T[] => isLike(ref, x, ipred);
126+
return assert(x, pred, "The value must follow the reference");
127+
}

0 commit comments

Comments
 (0)