Skip to content

Commit

Permalink
Rename fieldsAuto to fields (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
lydell authored Oct 30, 2023
1 parent eb74ed8 commit 26838d9
Show file tree
Hide file tree
Showing 14 changed files with 98 additions and 99 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
Note: I’m currently working on several breaking changes to tiny-decoders, but I’m trying out releasing them piece by piece. The idea is that you can either upgrade version by version only having to deal with one or a few breaking changes at a time, or wait and do a bunch of them at the same time.

### Version 23.0.0 (unreleased)

This release renames `fieldsAuto` to `fields`. Both of those functions used to exist, but `fields` was deprecated in version 11.0.0 and removed in version 14.0.0. There’s no need for the `Auto` suffix anymore.

### Version 22.0.0 (2023-10-30)

This release renames `fieldsUnion` to `taggedUnion` since it better describes what it is, and it goes along better with the `tag` function.
Expand Down
36 changes: 18 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
array,
boolean,
field,
fieldsAuto,
fields,
format,
type Infer,
number,
Expand All @@ -35,7 +35,7 @@ import {
// You can also import into a namespace if you want (conventionally called `Codec`):
import * as Codec from "tiny-decoders";

const userCodec = fieldsAuto({
const userCodec = fields({
name: string,
active: field(boolean, { renameFrom: "is_active" }),
age: field(number, { optional: true }),
Expand Down Expand Up @@ -115,7 +115,7 @@ Here’s a summary of all codecs (with slightly simplified type annotations) and
- Codec type: [Codec and DecoderResult](#codect-and-decoderresultt)
- Primitives: [unknown](#unknown), [boolean](#boolean), [number](#number), [bigint](#bigint), [string](#string)
- Collections: [array](#array), [record](#record), [tuple](#tuple)
- Object literals: [fieldsAuto](#fieldsauto) with [field](#field)
- Object literals: [fields](#fields) with [field](#field)
- Unions:
- Of primitive literals: [primitiveUnion](#primitiveunion)
- Of different types: [multi](#multi)
Expand Down Expand Up @@ -210,7 +210,7 @@ Here’s a summary of all codecs (with slightly simplified type annotations) and
<td><code>Record&lt;string, T&gt;</code></td>
</tr>
<tr>
<th><a href="#fieldsauto">fieldsAuto</a></th>
<th><a href="#fields">fields</a></th>
<td><pre>(mapping: {
field1: Codec&lt;T1&gt;,
field2: Field&lt;T2, {optional: true}&gt;,
Expand Down Expand Up @@ -247,14 +247,14 @@ Here’s a summary of all codecs (with slightly simplified type annotations) and
meta: Meta,
): Field&lt;Decoded, Meta&gt;</pre></td>
<td>n/a</td>
<td>n/a, only used with <code>fieldsAuto</code></td>
<td>n/a, only used with <code>fields</code></td>
</tr>
<tr>
<th><a href="#taggedunion">taggedUnion</a></th>
<td><pre>(
decodedCommonField: string,
variants: Array&lt;
Parameters&lt;typeof fieldsAuto&gt;[0]
Parameters&lt;typeof fields&gt;[0]
&gt;,
) =&gt;
Codec&lt;T1 | T2 | TN&gt;</pre></td>
Expand Down Expand Up @@ -449,10 +449,10 @@ The passed `codec` is for each value of the object.

For example, `record(number)` is a codec for an object where the keys can be anything and the values are numbers (`Record<string, number>`).

### fieldsAuto
### fields

```ts
function fieldsAuto<Mapping extends FieldsMapping>(
function fields<Mapping extends FieldsMapping>(
mapping: Mapping,
{ allowExtraFields = true }: { allowExtraFields?: boolean } = {},
): Codec<InferFields<Mapping>, InferEncodedFields<Mapping>>;
Expand Down Expand Up @@ -491,7 +491,7 @@ type User = {
active: boolean;
};

const userCodec: Codec<User> = fieldsAuto({
const userCodec: Codec<User> = fields({
name: string,
age: field(number, { optional: true }),
active: field(boolean, { renameFrom: "is_active" }),
Expand Down Expand Up @@ -532,14 +532,14 @@ This function takes a codec and lets you:
- Rename a field: `field(string, { renameFrom: "some_name" })`
- Both: `field(string, { optional: true, renameFrom: "some_name" })`

Use it with [fieldsAuto](#fieldsAuto).
Use it with [fields](#fields).

The `tag` thing is handled by the [tag](#tag) function. It’s not something you’ll set manually using `field`. (That’s why the type annotation says `Omit<FieldMeta, "tag">`.)

Here’s an example illustrating the difference between `field(string, { optional: true })` and `undefinedOr(string)`:

```ts
const exampleCodec = fieldsAuto({
const exampleCodec = fields({
// Required field.
a: string,

Expand Down Expand Up @@ -571,7 +571,7 @@ type Example = {
> Why? Let’s take this codec as an example:
>
> ```ts
> const exampleCodec = fieldsAuto({
> const exampleCodec = fields({
> name: field(string, { optional: true }),
> });
> ```
Expand All @@ -593,7 +593,7 @@ type Example = {
> Notice the added `| undefined`. That allows also constructing `{ name: undefined }`. But if you run `exampleCodec.decoder({ name: undefined })`, the decoder will fail. The decoder only supports `name` existing and being set to a string, or `name` being missing. It does not support it being set to `undefined` explicitly. If you wanted to support that, use `undefinedOr`:
>
> ```ts
> const exampleCodec = fieldsAuto({
> const exampleCodec = fields({
> name: field(undefinedOr(string), { optional: true }),
> });
> ```
Expand Down Expand Up @@ -632,14 +632,14 @@ type InferFieldsUnion<MappingsUnion extends FieldsMapping> = magic;
type InferEncodedFieldsUnion<MappingsUnion extends FieldsMapping> = magic;
// See `fieldsAuto` for the definitions of `Field`, `FieldMeta` and `FieldsMapping`.
// See `fields` for the definitions of `Field`, `FieldMeta` and `FieldsMapping`.
```
Codec for JSON objects with a common field (that tells them apart), and a TypeScript tagged union type.

The `decodedCommonField` is the name of the common field.

`variants` is an array of objects. Those objects are “`fieldsAuto` objects” – they fit when passed to `fieldsAuto` as well. All of those objects must have `decodedCommonField` as a key, and use the [tag](#tag) function on that key.
`variants` is an array of objects. Those objects are “`fields` objects” – they fit when passed to `fields` as well. All of those objects must have `decodedCommonField` as a key, and use the [tag](#tag) function on that key.

```ts
type Shape =
Expand All @@ -659,7 +659,7 @@ const shapeCodec: Codec<Shape> = taggedUnion("tag", [
]);
```

The `allowExtraFields` option works just like for [fieldsAuto](#fieldsauto).
The `allowExtraFields` option works just like for [fields](#fields).

See also these examples:

Expand Down Expand Up @@ -1121,7 +1121,7 @@ You can use ESLint’s [no-restricted-globals](https://eslint.org/docs/latest/ru
Rather than first defining the type and then defining the codec (which often feels like writing the type twice), you can _only_ define the decoder and then infer the type.

```ts
const personCodec = fieldsAuto({
const personCodec = fields({
name: string,
age: number,
});
Expand All @@ -1138,7 +1138,7 @@ This is a nice pattern (naming the type and the codec the same):

```ts
type Person = Infer<typeof Person>;
const Person = fieldsAuto({
const Person = fields({
name: string,
age: number,
});
Expand Down
6 changes: 3 additions & 3 deletions examples/decode-constrained.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ import { expect, test } from "vitest";
import {
Codec,
DecoderResult,
fieldsAuto,
fields,
format,
primitiveUnion,
string,
} from "../";

test("decode constrained", () => {
const codec1 = fieldsAuto({
const codec1 = fields({
status: string, // In a first codec, we have a pretty loose type.
});

const codec2 = fieldsAuto({
const codec2 = fields({
status: primitiveUnion(["ok", "error"]), // In a second codec, we have a stricter type.
});

Expand Down
6 changes: 3 additions & 3 deletions examples/extra-fields.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, test } from "vitest";

import { Codec, fieldsAuto, map, number, string } from "..";
import { Codec, fields, map, number, string } from "..";
import { run } from "../tests/helpers";

test("adding extra fields to records", () => {
Expand All @@ -16,7 +16,7 @@ test("adding extra fields to records", () => {

// Use `map` to add it:
const productCodec: Codec<Product> = map(
fieldsAuto({
fields({
name: string,
price: number,
}),
Expand Down Expand Up @@ -53,7 +53,7 @@ test("adding extra fields to records", () => {
// In previous versions of tiny-decoders, another way of doing this was to add
// a decoder that always succeeds (a function that ignores its input and
// always returns the same value).
const productCodecBroken: Codec<Product> = fieldsAuto({
const productCodecBroken: Codec<Product> = fields({
name: string,
price: number,
version: {
Expand Down
16 changes: 8 additions & 8 deletions examples/readme.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
Codec,
DecoderResult,
field,
fieldsAuto,
fields,
Infer,
number,
string,
Expand All @@ -23,7 +23,7 @@ test("the main readme example", () => {
interests: Array<string>;
};

const userCodec: Codec<User> = fieldsAuto({
const userCodec: Codec<User> = fields({
name: string,
active: field(boolean, { renameFrom: "is_active" }),
age: field(number, { optional: true }),
Expand Down Expand Up @@ -72,9 +72,9 @@ function getSomeInvalidJSON(): unknown {
}

test("default vs sensitive error messages", () => {
const userCodec = fieldsAuto({
const userCodec = fields({
name: string,
details: fieldsAuto({
details: fields({
email: string,
ssn: string,
}),
Expand Down Expand Up @@ -102,16 +102,16 @@ test("default vs sensitive error messages", () => {
`);
});

test("fieldsAuto exactOptionalPropertyTypes", () => {
const exampleCodec = fieldsAuto({
test("fields exactOptionalPropertyTypes", () => {
const exampleCodec = fields({
name: field(string, { optional: true }),
});

type Example = { name?: string };

expectType<TypeEqual<Infer<typeof exampleCodec>, Example>>(true);

const exampleCodec2 = fieldsAuto({
const exampleCodec2 = fields({
name: field(undefinedOr(string), { optional: true }),
});

Expand All @@ -130,7 +130,7 @@ test("fieldsAuto exactOptionalPropertyTypes", () => {
});

test("fieldAuto optional vs undefined", () => {
const exampleCodec = fieldsAuto({
const exampleCodec = fields({
// Required field.
a: string,

Expand Down
8 changes: 4 additions & 4 deletions examples/recursive.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
array,
Codec,
DecoderResult,
fieldsAuto,
fields,
flatMap,
multi,
record,
Expand All @@ -22,15 +22,15 @@ test("recursive data structure", () => {
// This wouldn’t work to decode it, because we’re trying to use
// `personDecoder` in the definition of `personDecoder` itself.
/*
const personCodec = fieldsAuto<Person>({
const personCodec = fields<Person>({
name: string,
friends: array(personDecoder2), // ReferenceError: Cannot access 'personDecoder2' before initialization
});
*/

// `recursive` lets us delay when `personDecoder` is referenced, solving the
// issue.
const personCodec: Codec<Person> = fieldsAuto({
const personCodec: Codec<Person> = fields({
name: string,
friends: array(recursive(() => personCodec)),
});
Expand Down Expand Up @@ -139,7 +139,7 @@ test("circular objects", () => {
likes: Person;
};

const personCodec: Codec<Person> = fieldsAuto({
const personCodec: Codec<Person> = fields({
name: string,
likes: recursive(() => personCodec),
});
Expand Down
4 changes: 2 additions & 2 deletions examples/taggedUnion-with-common-fields.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { expect, test } from "vitest";
import {
boolean,
Codec,
fieldsAuto,
fields,
Infer,
InferEncoded,
number,
Expand Down Expand Up @@ -72,7 +72,7 @@ test("taggedUnion with common fields", () => {
]);

type EventMetadata = Infer<typeof EventMetadata>;
const EventMetadata = fieldsAuto({
const EventMetadata = fields({
id: string,
timestamp: string,
});
Expand Down
4 changes: 2 additions & 2 deletions examples/tuples.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, test } from "vitest";

import { Codec, fieldsAuto, map, number, tuple } from "../";
import { Codec, fields, map, number, tuple } from "../";

test("decoding tuples", () => {
type PointTuple = [number, number];
Expand Down Expand Up @@ -64,7 +64,7 @@ test("decoding tuples", () => {
// You can of course decode an object to a tuple as well:
const obj: unknown = { x: 1, y: 2 };
const pointTupleCodec: Codec<PointTuple> = map(
fieldsAuto({
fields({
x: number,
y: number,
}),
Expand Down
12 changes: 6 additions & 6 deletions examples/type-annotations.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, test } from "vitest";

import { Codec, DecoderResult, fieldsAuto, number, string } from "../";
import { Codec, DecoderResult, fields, number, string } from "../";

test("type annotations", () => {
// First, a small test type and a function that receives it:
Expand All @@ -26,7 +26,7 @@ test("type annotations", () => {
// TypeScript will infer what it decodes into (try hovering `personCodec1`
// in your editor!), but it won’t know that you intended to decode a `Person`.
// As you can see, I’ve misspelled `age` as `aye`.
const personCodec1 = fieldsAuto({
const personCodec1 = fields({
name: string,
aye: number,
});
Expand All @@ -39,7 +39,7 @@ test("type annotations", () => {
// The way to make the above type error more clear is to provide an explicit type
// annotation, so that TypeScript knows what you’re trying to do.
// @ts-expect-error Type '{ name: string; aye: number; }' is not assignable to type 'Person'.
const personCodec2: Codec<Person> = fieldsAuto({
const personCodec2: Codec<Person> = fields({
name: string,
aye: number,
});
Expand All @@ -51,7 +51,7 @@ test("type annotations", () => {

// TypeScript allows passing extra properties, so without type annotations
// there are no errors:
const personCodec3 = fieldsAuto({
const personCodec3 = fields({
name: string,
age: number,
extra: string,
Expand All @@ -61,15 +61,15 @@ test("type annotations", () => {

// Adding `Codec<Person>` helps TypeScript find the error:
// @ts-expect-error Type 'Person' is not assignable to type '{ name: string; age: number; extra: string; }'.
const personCodec4: Codec<Person> = fieldsAuto({
const personCodec4: Codec<Person> = fields({
name: string,
age: number,
extra: string,
});
greet(personCodec4.decoder(testPerson));

// Finally, a compiling codec.
const personCodec5: Codec<Person> = fieldsAuto({
const personCodec5: Codec<Person> = fields({
name: string,
age: number,
});
Expand Down
Loading

0 comments on commit 26838d9

Please sign in to comment.