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

Rename fieldsAuto to fields #45

Merged
merged 1 commit into from
Oct 30, 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
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