From a6e1a9de83f95078c09dd6fcf144b79cd22c7646 Mon Sep 17 00:00:00 2001 From: Timm Stelzer Date: Tue, 30 Aug 2022 21:51:15 +0200 Subject: [PATCH 1/4] feat: add null decoder --- packages/runtime/_src/Decoder.ts | 27 ++++++++++++++++++++++++ packages/runtime/_src/Guard.ts | 7 ++++++ packages/runtime/_test/Decoder.test.ts | 12 +++++++++++ packages/stdlib/_src/type-level/check.ts | 5 +++++ 4 files changed, 51 insertions(+) diff --git a/packages/runtime/_src/Decoder.ts b/packages/runtime/_src/Decoder.ts index 7a42115..e2423b7 100644 --- a/packages/runtime/_src/Decoder.ts +++ b/packages/runtime/_src/Decoder.ts @@ -132,6 +132,17 @@ export class DecoderErrorLiteral implements Decoder.Error { } } +export class DecoderErrorNull implements Decoder.Error { + constructor( + readonly value: unknown + ) {} + render = () => { + return Tree( + `Expected null instead received one of type "${typeof this.value}"` + ) + } +} + export class DecoderErrorStructMissingField implements Decoder.Error { render = () => Tree(`Missing`) } @@ -250,6 +261,15 @@ export const number: Decoder = Decoder((u) => Result.fail(new DecoderErrorPrimitive(u, "number")) ) +/** + * @tsplus implicit + */ +export const _null: Decoder = Decoder((u) => + Derive>().is(u) ? + Result.success(u) : + Result.fail(new DecoderErrorNull(u)) +) + /** * @tsplus implicit */ @@ -608,6 +628,13 @@ export function deriveLiteral( ) } +/** + * @tsplus derive Decoder<_> 20 + */ +export function deriveNull(): Decoder { + return Decoder((u) => u === null ? Result.success(u as A) : Result.fail(new DecoderErrorNull(u))) +} + /** * @tsplus derive Decoder<_> 20 */ diff --git a/packages/runtime/_src/Guard.ts b/packages/runtime/_src/Guard.ts index b586504..da911d9 100644 --- a/packages/runtime/_src/Guard.ts +++ b/packages/runtime/_src/Guard.ts @@ -56,6 +56,13 @@ export const boolean: Guard = Guard((u): u is boolean => typeof u === " */ export const number: Guard = Guard((u): u is number => typeof u === "number") +/** + * Guard for null + * + * @tsplus implicit + */ +export const _null: Guard = Guard((u): u is null => u === null) + /** * Guard for a string * diff --git a/packages/runtime/_test/Decoder.test.ts b/packages/runtime/_test/Decoder.test.ts index 9bfe6ba..136c840 100644 --- a/packages/runtime/_test/Decoder.test.ts +++ b/packages/runtime/_test/Decoder.test.ts @@ -92,6 +92,18 @@ describe.concurrent("Decoder", () => { `Expected literal "1" of type "number" instead received "200"` ) }) + it("null", () => { + const decoder: Decoder = Derive() + assert.deepEqual(decoder.decodeJSON(JSON.stringify(null)).right.value, null) + assert.deepEqual( + decoder.decodeJSON(JSON.stringify("no-hello")).left.value?.message, + `Expected null instead received one of type "string"` + ) + assert.deepEqual( + decoder.decodeJSON(JSON.stringify(200)).left.value?.message, + `Expected null instead received one of type "number"` + ) + }) it("struct", () => { interface Person { firstName: string diff --git a/packages/stdlib/_src/type-level/check.ts b/packages/stdlib/_src/type-level/check.ts index 63edb13..f42fff1 100644 --- a/packages/stdlib/_src/type-level/check.ts +++ b/packages/stdlib/_src/type-level/check.ts @@ -53,6 +53,11 @@ export declare namespace Check { Extends | Extends > + /** + * @tsplus type Check.IsNull + */ + type IsNull = Not> + /** * @tsplus type Check.IsStruct */ From f97a85ce27d6fc915c19886f846e1175e8b58a6a Mon Sep 17 00:00:00 2001 From: Timm Stelzer Date: Fri, 2 Sep 2022 17:27:18 +0200 Subject: [PATCH 2/4] refactor: remove redundant rules/util --- packages/runtime/_src/Decoder.ts | 7 ------- packages/stdlib/_src/type-level/check.ts | 5 ----- 2 files changed, 12 deletions(-) diff --git a/packages/runtime/_src/Decoder.ts b/packages/runtime/_src/Decoder.ts index e2423b7..3a38b83 100644 --- a/packages/runtime/_src/Decoder.ts +++ b/packages/runtime/_src/Decoder.ts @@ -628,13 +628,6 @@ export function deriveLiteral( ) } -/** - * @tsplus derive Decoder<_> 20 - */ -export function deriveNull(): Decoder { - return Decoder((u) => u === null ? Result.success(u as A) : Result.fail(new DecoderErrorNull(u))) -} - /** * @tsplus derive Decoder<_> 20 */ diff --git a/packages/stdlib/_src/type-level/check.ts b/packages/stdlib/_src/type-level/check.ts index f42fff1..63edb13 100644 --- a/packages/stdlib/_src/type-level/check.ts +++ b/packages/stdlib/_src/type-level/check.ts @@ -53,11 +53,6 @@ export declare namespace Check { Extends | Extends > - /** - * @tsplus type Check.IsNull - */ - type IsNull = Not> - /** * @tsplus type Check.IsStruct */ From 2a26a63419073e89440e4207fdc5bb47bfa79c0f Mon Sep 17 00:00:00 2001 From: Timm Stelzer Date: Sat, 3 Sep 2022 15:26:04 +0200 Subject: [PATCH 3/4] feat: add null encoder --- packages/runtime/_src/Encoder.ts | 7 +++++++ packages/runtime/_test/Encoder.test.ts | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/packages/runtime/_src/Encoder.ts b/packages/runtime/_src/Encoder.ts index f0cc553..f9ddc78 100644 --- a/packages/runtime/_src/Encoder.ts +++ b/packages/runtime/_src/Encoder.ts @@ -52,6 +52,13 @@ export const boolean: Encoder = Encoder((u) => u) */ export const number: Encoder = Encoder((u) => u) +/** + * Encoder for null + * + * @tsplus implicit + */ +export const _null: Encoder = Encoder((u) => u) + /** * Encoder for a string * diff --git a/packages/runtime/_test/Encoder.test.ts b/packages/runtime/_test/Encoder.test.ts index 6130ae4..4655a4d 100644 --- a/packages/runtime/_test/Encoder.test.ts +++ b/packages/runtime/_test/Encoder.test.ts @@ -12,6 +12,10 @@ describe.concurrent("Encoder", () => { const number: Encoder = Derive() assert.deepEqual(number.encode(1), 1) }) + it("null", () => { + const { encode }: Encoder = Derive() + assert.deepEqual(encode(null), null) + }) it("date", () => { const string: Encoder = Derive() const date = new Date() From bff63d32c3b3c1806657e5631079f718ca89e79e Mon Sep 17 00:00:00 2001 From: Timm Stelzer Date: Sat, 3 Sep 2022 15:30:49 +0200 Subject: [PATCH 4/4] chore: update changeset --- .changeset/wild-olives-exercise.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/wild-olives-exercise.md diff --git a/.changeset/wild-olives-exercise.md b/.changeset/wild-olives-exercise.md new file mode 100644 index 0000000..1c04ab8 --- /dev/null +++ b/.changeset/wild-olives-exercise.md @@ -0,0 +1,5 @@ +--- +"@tsplus/runtime": patch +--- + +Add Encoder, Guard and Decoder