Skip to content

Commit

Permalink
Make the number Cake reject NaN (#64)
Browse files Browse the repository at this point in the history
  • Loading branch information
justinyaodu authored Feb 9, 2023
1 parent 85eca42 commit f421682
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 28 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,13 @@ number.is(5); // true
number.is("5"); // false
```

Although `typeof NaN === "number"`, this Cake does not accept `NaN`:

```ts
number.is(NaN); // false
number.as(NaN); // TypeError: Value is NaN.
```

---

#### `string`
Expand Down Expand Up @@ -2015,7 +2022,8 @@ const c: Class<Date, [number]> = Date;

#### Changed

- Built-in named Cakes (e.g. [number](#number)) now have type `Cake` instead of `TypeGuardCake` ([#60](https://github.com/justinyaodu/caketype/pull/60))
- [number](#number) no longer accepts `NaN` ([#64](https://github.com/justinyaodu/caketype/pull/64))
- Built-in named Cakes (e.g. [boolean](#boolean)) now have type `Cake` instead of `TypeGuardCake` ([#60](https://github.com/justinyaodu/caketype/pull/60))
- Replaced `TypeGuardFailedCakeError` with `WrongTypeCakeError`, which is more general and has a more concise error message ([#60](https://github.com/justinyaodu/caketype/pull/60))

---
Expand Down
12 changes: 11 additions & 1 deletion etc/caketype.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,17 @@ export class NotAnObjectCakeError extends CakeError {
}

// @public
export const number: Cake<number>;
export const number: NumberCake;

// @public
export class NumberCake extends Cake<number> {
// (undocumented)
dispatchCheck(value: unknown, context: CakeDispatchCheckContext): CakeError | null;
// (undocumented)
dispatchStringify(context: CakeDispatchStringifyContext): string;
// (undocumented)
withName(name: string | null): NumberCake;
}

// @public
export type ObjectBakeable = {
Expand Down
71 changes: 71 additions & 0 deletions src/cake/NumberCake.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import {
Cake,
CakeDispatchCheckContext,
CakeDispatchStringifyContext,
CakeError,
CakeErrorDispatchFormatContext,
StringTree,
WrongTypeCakeError,
} from "./index-internal";

/**
* See {@link number}.
*
* @public
*/
class NumberCake extends Cake<number> {
dispatchCheck(
value: unknown,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
context: CakeDispatchCheckContext
): CakeError | null {
if (typeof value !== "number") {
return new WrongTypeCakeError(this, value);
}
if (Number.isNaN(value)) {
return new IsNaNCakeError();
}
return null;
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
dispatchStringify(context: CakeDispatchStringifyContext): string {
return "number";
}

withName(name: string | null): NumberCake {
return new NumberCake({ ...this, name });
}
}

class IsNaNCakeError extends CakeError {
constructor() {
super();
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
dispatchFormat(context: CakeErrorDispatchFormatContext): StringTree {
return "Value is NaN.";
}
}

/**
* A {@link Cake} representing the `number` type.
*
* @example
* ```ts
* number.is(5); // true
* number.is("5"); // false
* ```
*
* @example Although `typeof NaN === "number"`, this Cake does not accept `NaN`:
* ```ts
* number.is(NaN); // false
* number.as(NaN); // TypeError: Value is NaN.
* ```
*
* @public
*/
const number = new NumberCake({});

export { NumberCake, number };
15 changes: 0 additions & 15 deletions src/cake/TypeGuardCake.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
is_boolean,
is_bigint,
is_never,
is_number,
is_string,
is_symbol,
is_unknown,
Expand Down Expand Up @@ -141,19 +140,6 @@ const bigint: Cake<bigint> = typeGuard("bigint", is_bigint);
*/
const never: Cake<never> = typeGuard("never", is_never);

/**
* A {@link Cake} representing the `number` type.
*
* @example
* ```ts
* number.is(5); // true
* number.is("5"); // false
* ```
*
* @public
*/
const number: Cake<number> = typeGuard("number", is_number);

/**
* A {@link Cake} representing the `string` type.
*
Expand Down Expand Up @@ -206,7 +192,6 @@ export {
boolean,
bigint,
never,
number,
string,
symbol,
unknown,
Expand Down
1 change: 1 addition & 0 deletions src/cake/index-internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export * from "./CakeStringifier";
export * from "./Checker";
export * from "./CheckOptions";
export * from "./LiteralCake";
export * from "./NumberCake";
export * from "./ObjectCake";
export * from "./ReferenceCake";
export * from "./StringTree";
Expand Down
4 changes: 3 additions & 1 deletion src/cake/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export {
LiteralCake,
LiteralCakeArgs,
LiteralNotEqualCakeError,
// NumberCake.ts
NumberCake,
number,
// ObjectCake.ts
ObjectCake,
ObjectCakeArgs,
Expand Down Expand Up @@ -56,7 +59,6 @@ export {
boolean,
bigint,
never,
number,
string,
symbol,
unknown,
Expand Down
5 changes: 0 additions & 5 deletions src/type-guards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ function is_never(value: unknown): value is never {
return false;
}

function is_number(value: unknown): value is number {
return typeof value === "number";
}

function is_object(value: unknown): value is object {
switch (typeof value) {
case "object":
Expand Down Expand Up @@ -47,7 +43,6 @@ export {
is_bigint,
is_boolean,
is_never,
is_number,
is_object,
is_string,
is_symbol,
Expand Down
2 changes: 2 additions & 0 deletions tests/cake/Cake-withName.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
bake,
boolean,
keysUnsound,
number,
reference,
TupleCake,
TypeGuardCake,
Expand All @@ -13,6 +14,7 @@ import { is_boolean } from "../../src/type-guards";
const cakes = {
array: array({}),
literal: bake(0),
number: number,
object: bake({}),
reference: reference<boolean>(() => boolean),
tuple: new TupleCake({
Expand Down
14 changes: 14 additions & 0 deletions tests/cake/NumberCake.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { number } from "../../src";
import { expectTypeError } from "../test-helpers";

describe("documentation examples", () => {
test("number", () => {
expect(number.is(5)).toStrictEqual(true);
expect(number.is("5")).toStrictEqual(false);
});

test("number NaN", () => {
expect(number.is(NaN)).toStrictEqual(false);
expectTypeError(() => number.as(NaN), "Value is NaN.");
});
});
5 changes: 0 additions & 5 deletions tests/cake/TypeGuardCake.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,6 @@ describe("documentation examples", () => {
expect(never.is(undefined)).toStrictEqual(false);
});

test("number", () => {
expect(number.is(5)).toStrictEqual(true);
expect(number.is("5")).toStrictEqual(false);
});

test("string", () => {
expect(string.is("hello")).toStrictEqual(true);
expect(string.is("")).toStrictEqual(true);
Expand Down

0 comments on commit f421682

Please sign in to comment.