Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the number Cake reject NaN #64

Merged
merged 3 commits into from
Feb 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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