### Install Funtypes Source: https://funtypes.dev/docs/getting-started Use npm to add the funtypes package to your project. ```bash npm install --save funtypes ``` -------------------------------- ### Parsing and validating with Codec.parse Source: https://funtypes.dev/docs/funtypes-api-parse Demonstrates defining a schema with funtypes and using .parse() to validate and transform input data. Includes examples of successful parsing and validation errors. ```typescript import * as ft from "funtypes"; import * as s from "funtypes-schemas"; export const UserCodec = ft.Object({ id: ft.Number, name: ft.String, dateOfBirth: s.ParsedDateTimeString(), }); // => ft.Codec<{ id: number; name: string; dateOfBirth: Date }> // ✅ Validates and parses the Date assert.deepEqual( UserCodec.parse({ id: 1, name: "Forbes Lindesay", dateOfBirth: "1970-01-01T00:00:00.000Z", }), { id: 1, name: "Forbes Lindesay", dateOfBirth: new Date( "1970-01-01T00:00:00.000Z", ), }, ); // 🚨 Invalid: id should be a number, but here // we've passed a string instead. assert.throws(() => { UserCodec.parse({ id: "42", name: "Forbes Lindesay", dateOfBirth: "1970-01-01T00:00:00.000Z", }); }); // 🚨 Invalid: dateOfBirth is already a Date, but // the serialized value should have been as // string. assert.throws(() => { UserCodec.parse({ id: 1, name: "Forbes Lindesay", dateOfBirth: new Date( "1970-01-01T00:00:00.000Z", ), }); }); ``` -------------------------------- ### Define TypeScript types Source: https://funtypes.dev/docs/getting-started Example TypeScript type definitions for space-related objects. ```typescript type Vector = [number, number, number]; type Asteroid = { type: "asteroid"; location: Vector; mass: number; }; type Planet = { type: "planet"; location: Vector; mass: number; population: number; habitable: boolean; }; type Rank = | "captain" | "first mate" | "officer" | "ensign"; type CrewMember = { name: string; age: number; rank: Rank; home: Planet; }; type Ship = { type: "ship"; location: Vector; mass: number; name: string; crew: CrewMember[]; }; type SpaceObject = Asteroid | Planet | Ship; ``` -------------------------------- ### ft.Pick(codec, keys) Source: https://funtypes.dev/docs/types-pick Creates a new codec that includes only the specified keys from the source object codec. ```APIDOC ## ft.Pick ### Description Creates a new object codec that only includes the specified properties from the source object codec. This is useful for creating subsets of existing data structures. ### Signature `ft.Pick(codec: ObjectCodec, keys: string[])` ### Parameters - **codec** (ObjectCodec) - The source object codec to pick properties from. - **keys** (string[]) - An array of property names to include in the resulting codec. ### Example ```typescript import * as ft from "funtypes"; const UserCodec = ft.Object({ id: ft.Number, name: ft.String, }); const NamedCodec = ft.Pick(UserCodec, ["name"]); // NamedCodec validates objects with only the 'name' property. ``` ``` -------------------------------- ### Pick properties from an object codec Source: https://funtypes.dev/docs/types-pick Demonstrates selecting specific keys from a standard object codec and verifying the resulting type and parsing behavior. ```typescript import * as ft from "funtypes"; const UserCodec = ft.Object({ id: ft.Number, name: ft.String, }); // => Codec<{ id: number; name: string }> const NamedCodec = ft.Pick(UserCodec, ["name"]); // => Codec<{ name: string }> type Named = ft.Static; // => { name: string } assert.deepEqual( ft.showType(NamedCodec), "{ name: string }", ); // ✅ Valid object with correct keys assert.deepEqual( NamedCodec.parse({ name: "Forbes Lindesay", }), { name: "Forbes Lindesay", }, ); // ✅ Extra keys are ignored when parsing // (use `Sealed` to prevent this) assert.deepEqual( NamedCodec.parse({ id: 1, name: "Forbes Lindesay", }), { name: "Forbes Lindesay", }, ); ``` -------------------------------- ### Define a Union of Literals Source: https://funtypes.dev/docs/types-literal Use ft.Literal within a union to restrict values to a specific set of constants. This example demonstrates both successful parsing and error handling for invalid inputs. ```typescript import * as ft from "funtypes"; export const ObjectKindCodec = ft.Union( ft.Literal("USER"), ft.Literal("POST"), ); // => ft.Codec<"USER" | "POST"> export type ObjectKind = ft.Static< typeof ObjectKindCodec >; // => "USER" | "POST" // ✅ Valid value assert.deepEqual( ObjectKindCodec.parse("USER"), "USER", ); assert.deepEqual( ObjectKindCodec.parse("POST"), "POST", ); // 🚨 Invalid value assert.throws(() => ObjectKindCodec.parse(42)); // 🚨 Invalid value assert.throws(() => ObjectKindCodec.parse("SOME_OTHER_STRING"), ); ``` -------------------------------- ### ft.Record(keyCodec, valueCodec) Source: https://funtypes.dev/docs/types-record Creates a codec that validates an object where keys match the provided keyCodec and values match the valueCodec. ```APIDOC ## ft.Record(keyCodec, valueCodec) ### Description Validates that an input is an object where all keys conform to the `keyCodec` and all values conform to the `valueCodec`. ### Parameters - **keyCodec** (Codec) - Required - A codec for the object keys (must be string or number based). - **valueCodec** (Codec) - Required - A codec for the object values. ### Example ```typescript import * as ft from "funtypes"; const MyRecordCodec = ft.Record(ft.String, ft.Number); const result = MyRecordCodec.parse({ "Forty Two": 42 }); ``` ``` -------------------------------- ### Compose codecs using withParser Source: https://funtypes.dev/docs/types-unknown Simplifies codec creation by composing an existing constraint-based codec with a parser. ```typescript const BufferCodec = ft.Unknown.withConstraint( (value) => Buffer.isBuffer(value) || "Expected a node.js Buffer", { name: "Buffer" }, ); const Utf8BufferCodec = BufferCodec.withParser({ name: "Utf8Buffer", test: ft.String, parse(value) { return { success: true, value: value.toString("utf8"), }; }, serialize(value) { return { success: true, value: Buffer.from(value, "utf8"), }; }, }); // => ft.Codec assert.deepEqual( Utf8BufferCodec.parse( Buffer.from("Hello World"), ), "Hello World", ); assert.deepEqual( Utf8BufferCodec.safeParse("Hello World"), { success: false, message: "Expected a node.js Buffer", }, ); ``` -------------------------------- ### ft.showType(codec) Source: https://funtypes.dev/docs/funtypes-api-showtype Generates a human-readable string representation of a given funtypes Codec. ```APIDOC ## ft.showType(codec) ### Description Returns a string representation of a funtypes Codec that matches TypeScript type syntax. ### Parameters - **codec** (Codec) - Required - The funtypes Codec instance to be converted to a string. ### Example ```typescript import * as ft from "funtypes"; export const UserCodec = ft.Object({ id: ft.Number, name: ft.String, }); const typeString = ft.showType(UserCodec); // Result: "{ id: number; name: string }" ``` ``` -------------------------------- ### Basic usage of ft.Unknown Source: https://funtypes.dev/docs/types-unknown Demonstrates how ft.Unknown accepts any input value and passes it through unchanged. ```typescript import * as ft from "funtypes"; const MyCodec = ft.Unknown; // => ft.Codec type MyType = ft.Static; // => unknown // ✅ Accepts any value assert.deepEqual( MyCodec.parse({ hello: "world" }), { hello: "worldxxx" }, ); // ✅ Accepts any value assert.deepEqual(MyCodec.parse(42), 42); ``` -------------------------------- ### Validating runtime values with Codec.test Source: https://funtypes.dev/docs/funtypes-api-test Demonstrates how to define a codec and use .test() to validate objects against the schema, returning true for valid inputs and false for invalid ones. ```typescript import * as ft from "funtypes"; import * as s from "funtypes-schemas"; export const UserCodec = ft.Object({ id: ft.Number, name: ft.String, dateOfBirth: s.ParsedDateTimeString(), }); // => ft.Codec<{ id: number; name: string; dateOfBirth: Date }> // ✅ Valid so no error is thrown assert.deepEqual( UserCodec.test({ id: 1, name: "Forbes Lindesay", dateOfBirth: new Date( "1970-01-01T00:00:00.000Z", ), }), true, ); // 🚨 Invalid: id should be a // number, but here we've // passed a string instead. assert.deepEqual( UserCodec.test({ id: "42", name: "Forbes Lindesay", dateOfBirth: new Date( "1970-01-01T00:00:00.000Z", ), }), false, ); // 🚨 Invalid: dateOfBirth is a // string, which would // be ok if the value was serialized, but the // Codec.assert function tests if the value // matches the **parsed** Codec assert.deepEqual( UserCodec.test({ id: 1, name: "Forbes Lindesay", dateOfBirth: "1970-01-01T00:00:00.000Z", }), false, ); ``` -------------------------------- ### ft.Partial(codec) Source: https://funtypes.dev/docs/types-partial Creates a new codec where all properties of the provided object codec are optional. ```APIDOC ## ft.Partial(codec) ### Description Takes an existing object codec and returns a new codec where all properties are optional. This is useful for validation scenarios where partial updates or incomplete objects are expected. ### Usage ```typescript import * as ft from "funtypes"; const UserCodec = ft.Object({ id: ft.Number, name: ft.String, }); const PartialUserCodec = ft.Partial(UserCodec); // Parses objects with any subset of keys const result = PartialUserCodec.parse({ name: "Forbes Lindesay" }); ``` ### Parameters - **codec** (ObjectCodec) - Required - The object codec to make partial. ### Returns - **Codec** - A new codec instance with optional properties. ``` -------------------------------- ### Compare Optional and WithDefault Source: https://funtypes.dev/docs/types-with-default Shows the difference between ft.Optional, which allows absence, and ft.WithDefault, which enforces a value. ```typescript import * as ft from "funtypes"; const UserCodec = ft.Object({ nickname: ft.Optional(ft.String), role: ft.WithDefault(ft.String, "member"), }); // => ft.Codec<{ nickname?: string; role: string }> assert.deepEqual(UserCodec.parse({}), { role: "member", }); ``` -------------------------------- ### Define and Parse a User Schema Source: https://funtypes.dev/ Demonstrates defining a schema with object properties and parsing input data into a typed object. ```typescript import * as ft from "funtypes" import * as s from "funtypes-schemas" export const UserSchema = ft.Object({ id: ft.Number, name: ft.String, profile: s.ParsedUrlString(), }) export type User = ft.Static // => { id: number; name: string; profile: URL } assert.deepEqual( UserSchema.parse({ id: 42, name: "Forbes Lindesay", profile: "https://funtypes.dev", }), { id: 42, name: "Forbes Lindesay", profile: new URL("https://funtypes.dev"), }, ) ``` -------------------------------- ### Parse and Serialize with Funtypes Codecs Source: https://funtypes.dev/ Demonstrates defining a codec that handles both parsing and serialization for an object containing a URL string. ```typescript import * as ft from "funtypes"; import * as s from "funtypes-schemas"; const PostCodec = ft.Object({ id: ft.Number, title: ft.String, url: s.ParsedUrlString(), }); assert.deepEqual( PostCodec.parse({ id: 42, title: "Example Post", url: "http://example.com", }), { id: 42, title: "Example Post", url: new URL("http://example.com"), }, ); assert.deepEqual( PostCodec.serialize({ id: 42, title: "Example Post", url: new URL("http://example.com"), }), { id: 42, title: "Example Post", url: "http://example.com", }, ); ``` -------------------------------- ### ft.toStandardJsonSchema(codec, options?) Source: https://funtypes.dev/docs/standard-schema Wraps a Codec in an object that implements the Standard Schema spec, including validation and JSON Schema generation. ```APIDOC ## ft.toStandardJsonSchema(codec, options?) ### Description Wraps a `Codec` in an object that implements both halves of the Standard Schema spec: `~standard.validate`, and `~standard.jsonSchema.input`/`.output`. ### Parameters - **codec** (Codec) - Required - The funtypes Codec to wrap. - **options** (Object) - Optional - Configuration options for the schema generation. - **validateMode** (string) - Optional - Determines the validation behavior: "safeParse" (default), "serialize", or "assert". - **jsonSchemaMode** (Object) - Optional - Configures input/output shape definitions. - **input** (string) - Optional - "serialized" (default) or "parsed". - **output** (string) - Optional - "parsed" (default) or "serialized". - **namedRefs** (boolean) - Optional - Whether to use $defs and $ref for named types. Defaults to true. ``` -------------------------------- ### Define a codec with a default value Source: https://funtypes.dev/docs/types-with-default Demonstrates using ft.WithDefault to provide a fallback value for a missing or null property. ```typescript import * as ft from "funtypes"; const UserCodec = ft.Object({ id: ft.Number, role: ft.WithDefault(ft.String, "member"), }); // => ft.Codec<{ id: number; role: string }> type User = ft.Static; // => { id: number; role: string } assert.deepEqual( ft.showType(UserCodec), "{ id: number; role: string }", ); // ✅ Missing property falls back to the default assert.deepEqual(UserCodec.parse({ id: 1 }), { id: 1, role: "member", }); // ✅ `null` also falls back to the default assert.deepEqual( UserCodec.parse({ id: 1, role: null }), { id: 1, role: "member" }, ); // ✅ Any other value is validated normally assert.deepEqual( UserCodec.parse({ id: 1, role: "admin" }), { id: 1, role: "admin" }, ); // 🚨 Invalid: `role` doesn't match the // underlying type assert.throws(() => { UserCodec.parse({ id: 1, role: 42 }); }); ``` -------------------------------- ### ft.WithDefault(codec, defaultValue) Source: https://funtypes.dev/docs/types-with-default Creates a codec that substitutes a default value when the input is null or undefined. The field is treated as required in the resulting static type. ```APIDOC ## ft.WithDefault(codec, defaultValue) ### Description Substitutes a default value in place of `null` or `undefined` when parsing. The field is always present in the parsed output. ### Parameters - **codec** (Codec) - The underlying codec to validate the value against. - **defaultValue** (any) - The value to use if the input is `null` or `undefined`. ### Example ```typescript import * as ft from "funtypes"; const UserCodec = ft.Object({ id: ft.Number, role: ft.WithDefault(ft.String, "member"), }); // Parses missing or null role as "member" const user = UserCodec.parse({ id: 1 }); // Result: { id: 1, role: "member" } ``` ``` -------------------------------- ### Validating and parsing data with safeParse Source: https://funtypes.dev/docs/funtypes-api-safeparse Demonstrates defining a schema with funtypes and using safeParse to handle both valid and invalid input data. ```typescript import * as ft from "funtypes"; import * as s from "funtypes-schemas"; export const UserCodec = ft.Object({ id: ft.Number, name: ft.String, dateOfBirth: s.ParsedDateTimeString(), }); // => ft.Codec<{ id: number; name: string; dateOfBirth: Date }> // ✅ Validates and parses the Date assert.deepEqual( UserCodec.safeParse({ id: 1, name: "Forbes Lindesay", dateOfBirth: "1970-01-01T00:00:00.000Z", }), { success: true, value: { id: 1, name: "Forbes Lindesay", dateOfBirth: new Date( "1970-01-01T00:00:00.000Z", ), }, }, ); // 🚨 Invalid: id should be a number, but here // we've passed a string instead. const failedResult = UserCodec.safeParse({ id: "42", name: "Forbes Lindesay", dateOfBirth: "1970-01-01T00:00:00.000Z", }); assert.deepEqual(failedResult.success, false); console.log(ft.showError(failedResult)); // => prints human readable error message ``` -------------------------------- ### Displaying a value with showValue Source: https://funtypes.dev/docs/funtypes-api-showvalue Demonstrates the basic usage of showValue to convert an object into a human-readable string format. ```typescript import * as ft from "funtypes"; assert.deepEqual( ft.showValue({ id: 42, name: "Forbes Lindesay", }), `{ id: 42, name: "Forbes Lindesay" }`, ); ``` -------------------------------- ### Codec.introspection Source: https://funtypes.dev/docs/types-with-default Access the internal configuration of a WithDefault codec instance. ```APIDOC ## Codec.introspection ### Description Returns an object containing the tag, the underlying codec, and the default value configured for the WithDefault instance. ### Example ```typescript const RoleCodec = ft.WithDefault(ft.String, "member"); // Returns: { tag: "default", underlying: ft.String, defaultValue: "member" } const info = RoleCodec.introspection; ``` ``` -------------------------------- ### Implement Standard Schema for a Codec Source: https://funtypes.dev/docs/standard-schema Wraps a Funtypes codec to support standard validation and JSON Schema output. ```typescript import * as ft from "funtypes"; const UserCodec = ft.Object({ id: ft.Number, role: ft.WithDefault(ft.String, "member"), }); const schema = ft.toStandardJsonSchema(UserCodec); // ✅ `validate` behaves like `safeParse` by // default, so the default is applied assert.deepEqual( schema["~standard"].validate({ id: 1 }), { value: { id: 1, role: "member" } }, ); assert.deepEqual( schema["~standard"].jsonSchema.output({ target: "draft-2020-12", }), { type: "object", properties: { id: { type: "number" }, role: { type: "string", default: "member" }, }, required: ["id", "role"], }, ); ``` -------------------------------- ### Infer Static Types from Codecs Source: https://funtypes.dev/ Shows how to generate a static TypeScript type from a Funtypes codec to avoid redundant definitions. ```typescript import * as ft from "funtypes"; const UserCodec = ft.Object({ id: ft.Number, name: ft.String, }); // => Codec<{ id: number; name: string }> type User = ft.Static; // => { id: number; name: string } // Returns a `User` type, throws if requestBody // is not a valid User function asUser(requestBody: unknown) { return UserCodec.parse(requestBody); } ``` -------------------------------- ### Serializing objects with Funtypes Source: https://funtypes.dev/docs/funtypes-api-serialize Demonstrates defining a schema with ParsedDateTimeString and using serialize to convert Date objects to strings, along with error handling for invalid inputs. ```typescript import * as ft from "funtypes"; import * as s from "funtypes-schemas"; export const UserCodec = ft.Object({ id: ft.Number, name: ft.String, dateOfBirth: s.ParsedDateTimeString(), }); // => ft.Codec<{ id: number; name: string; dateOfBirth: Date }> // ✅ Serializes the date object to a // string assert.deepEqual( UserCodec.parse({ id: 1, name: "Forbes Lindesay", dateOfBirth: new Date( "1970-01-01T00:00:00.000Z", ), }), { id: 1, name: "Forbes Lindesay", dateOfBirth: "1970-01-01T00:00:00.000Z", }, ); // 🚨 Invalid: id should be a number, but here // we've passed a string instead. assert.throws(() => { UserCodec.serialize({ // TypeScript will expect the object passed // to `serialize` to be valid. // @ts-expect-error id: "42", name: "Forbes Lindesay", dateOfBirth: new Date( "1970-01-01T00:00:00.000Z", ), }); }); // 🚨 Invalid: dateOfBirth is already a string, // but the parsed value should have been a // Date. assert.throws(() => { UserCodec.serialize({ id: 1, name: "Forbes Lindesay", // TypeScript will expect the object passed // to `serialize` to be valid. // @ts-expect-error dateOfBirth: "1970-01-01T00:00:00.000Z", }); }); ``` -------------------------------- ### Apply ft.Partial to named codecs Source: https://funtypes.dev/docs/types-partial Shows how ft.Partial preserves the name of a codec when applied to an ft.Named wrapper. ```typescript import * as ft from "funtypes"; const UserCodec = ft.Named( "User", ft.Object({ id: ft.Number, name: ft.String, }), ); // => Codec<{ id: number; name: string }> const PartialUserCodec = ft.Partial(UserCodec); // => Codec<{ id?: number; name?: string }> type PartialUser = ft.Static< typeof PartialUserCodec >; // => { id?: number; name?: string } assert.deepEqual( ft.showType(PartialUserCodec), "Partial", ); ``` -------------------------------- ### Define a UTF8 Buffer codec with ft.Unknown.withParser Source: https://funtypes.dev/docs/types-unknown Creates a codec that serializes to a Buffer and parses to a UTF8 string. ```typescript const Utf8BufferCodec = ft.Unknown.withParser({ name: "Utf8Buffer", test: ft.String, parse(value) { if (!Buffer.isBuffer(value)) { return { success: false, message: "Expected a node.js Buffer", }; } return { success: true, value: value.toString("utf8"), }; }, serialize(value) { return { success: true, value: Buffer.from(value, "utf8"), }; }, }); // => ft.Codec assert.deepEqual( Utf8BufferCodec.parse( Buffer.from("Hello World"), ), "Hello World", ); assert.deepEqual( Utf8BufferCodec.safeParse("Hello World"), { success: false, message: "Expected a node.js Buffer", }, ); ``` -------------------------------- ### Serializing with safeSerialize Source: https://funtypes.dev/docs/funtypes-api-safeserialize Demonstrates successful serialization of a Date object to a string and handling of validation errors using safeSerialize. ```typescript import * as ft from "funtypes"; import * as s from "funtypes-schemas"; export const UserCodec = ft.Object({ id: ft.Number, name: ft.String, dateOfBirth: s.ParsedDateTimeString(), }); // => ft.Codec<{ id: number; name: string; dateOfBirth: Date }> // ✅ Serializes the date object to a string assert.deepEqual( UserCodec.safeSerialize({ id: 1, name: "Forbes Lindesay", dateOfBirth: new Date( "1970-01-01T00:00:00.000Z", ), }), { success: true, value: { id: 1, name: "Forbes Lindesay", dateOfBirth: "1970-01-01T00:00:00.000Z", }, }, ); // 🚨 Invalid: id should be a number, but here // we've passed a string instead. const failedResult = UserCodec.safeSerialize({ // TypeScript will expect the object passed // to `safeSerialize` to be valid. // @ts-expect-error id: "42", name: "Forbes Lindesay", dateOfBirth: new Date( "1970-01-01T00:00:00.000Z", ), }); assert.deepEqual(failedResult.success, false); console.log(ft.showError(failedResult)); // => prints human readable error message ``` -------------------------------- ### Displaying a Codec type with showType Source: https://funtypes.dev/docs/funtypes-api-showtype Use showType to convert a Codec definition into a human-readable string matching TypeScript syntax. ```typescript import * as ft from "funtypes"; export const UserCodec = ft.Object({ id: ft.Number, name: ft.String, }); // => ft.Codec<{ id: number; name: string }> assert.deepEqual( ft.showType(UserCodec), "{ id: number; name: string }", ); ``` -------------------------------- ### ft.String Source: https://funtypes.dev/docs/types-primitive Codec for validating and parsing string values. ```APIDOC ## ft.String ### Description Validates that the input is a string. Returns the string value on success or a failure object on error. ### Methods - **parse(value: any)**: Returns the value if it is a string, otherwise throws an error. - **safeParse(value: any)**: Returns an object with `success: true` and the value, or `success: false` and an error message. ``` -------------------------------- ### Define Union with Unknown Source: https://funtypes.dev/docs/types-unknown Demonstrates creating a Union containing custom codecs and ft.Unknown, resulting in an unknown static type. ```typescript import * as ft from "funtypes"; const UpperString = ft.String.withParser({ test: ft.String.withConstraint( (v) => v === v.toUpperCase() || "Expected string to be upper case", ), parse(value) { return { success: true, value: value.toUpperCase(), }; }, serialize(value) { return { success: true, value, }; }, }); // => ft.Codec const NegativeNumber = ft.Number.withParser({ test: ft.Number.withConstraint( (v) => v <= 0 || "Expected number to be negative", ), parse(value) { return { success: true, value: Math.abs(value) * -1, }; }, serialize(value) { return { success: true, value, }; }, }); // => ft.Codec // Unknown will replace all the other types in the union // as far as TypeScript is concerned const OpenCodec = ft.Union( UpperString, ft.Unknown, NegativeNumber, ); // => ft.Codec type Open = ft.Static; // => unknown // ✅ When parsing string, it'll use that // first codec assert.deepEqual( OpenCodec.parse("forbes lindesay"), "FORBES LINDESAY", ); // ✅ When parsing number, it'll use unknown as // that's before the number codec in the Union assert.deepEqual(OpenCodec.parse(42), 42); // ✅ When parsing anything else, it uses unknown assert.deepEqual( OpenCodec.parse({ hello: "world", }), { hello: "world", }, ); ``` -------------------------------- ### Attach metadata to a Codec Source: https://funtypes.dev/docs/types-with-comment Use WithComment to add a title and description to a property within an object codec. ```typescript import * as ft from "funtypes"; const UserCodec = ft.Object({ id: ft.Number, name: ft.WithComment( { title: "Full name", description: "The user's full, legal name", }, ft.String, ), }); // => ft.Codec<{ id: number; name: string }> // Parsing/serializing behave exactly as if // WithComment wasn't there at all. assert.deepEqual( UserCodec.parse({ id: 1, name: "Forbes Lindesay" }), { id: 1, name: "Forbes Lindesay" }, ); assert.deepEqual( ft.toJsonSchema(UserCodec), { type: "object", properties: { id: { type: "number" }, name: { type: "string", title: "Full name", description: "The user's full, legal name", }, }, required: ["id", "name"], }, ); ``` -------------------------------- ### Configure JSON Schema generation mode Source: https://funtypes.dev/docs/standard-schema Pass options to control the schema output, such as switching to serialized mode or disabling named references. ```typescript ft.toJsonSchema(SomeCodec, { mode: "serialized" }); ``` ```typescript ft.toJsonSchema(SomeCodec, { namedRefs: false }); ``` -------------------------------- ### ft.Named(name, codec) Source: https://funtypes.dev/docs/types-named Wraps a codec with a name to be used in error messages and type displays. ```APIDOC ## ft.Named(name, codec) ### Description Assigns a name to a codec. This name is used in error messages and when displaying the type, which is particularly helpful for debugging large unions. ### Parameters - **name** (string) - Required - The name to assign to the codec. - **codec** (Codec) - Required - The funtypes codec to be named. ### Usage Example ```typescript import * as ft from "funtypes"; const UserCodec = ft.Named( "User", ft.Object({ type: ft.Literal("USER"), id: ft.Number, name: ft.String, }), ); const DbObjectCodec = ft.Union(UserCodec, PostCodec); // Resulting type display will show "User | Post" instead of the full object structure assert.deepEqual(ft.showType(DbObjectCodec), "User | Post"); ``` ``` -------------------------------- ### Constructing a Funtypes Codec Source: https://funtypes.dev/docs/funtypes-api-codec Define a schema using funtypes and funtypes-schemas to create a codec that handles parsing and validation. ```typescript import * as ft from "funtypes"; import * as s from "funtypes-schemas"; export const UserCodec = ft.Object({ id: ft.Number, name: ft.String, dateOfBirth: s.ParsedDateTimeString(), }); // => ft.Codec<{ id: number; name: string; dateOfBirth: Date }> ``` -------------------------------- ### Accessing Standard Schema interface in Funtypes Source: https://funtypes.dev/docs/standard-schema Demonstrates accessing the ~standard property on a Funtypes codec to perform validation and verify vendor metadata. ```typescript import * as ft from "funtypes"; const UserCodec = ft.Object({ id: ft.Number, name: ft.String, }); assert.deepEqual( UserCodec["~standard"].vendor, "funtypes", ); assert.deepEqual(UserCodec["~standard"].version, 1); // ✅ Valid value assert.deepEqual( UserCodec["~standard"].validate({ id: 1, name: "Bob", }), { value: { id: 1, name: "Bob" } }, ); // 🚨 Invalid value - `issues` is populated // instead of `value` const result = UserCodec["~standard"].validate({ id: "not a number", name: "Bob", }); assert.deepEqual("issues" in result, true); ``` -------------------------------- ### showValue(value: any): string Source: https://funtypes.dev/docs/funtypes-api-showvalue Converts a JavaScript value into a human-readable string representation, handling cycles and length limits. ```APIDOC ## showValue(value) ### Description Generates a human-readable string representation of a JavaScript value. This utility is optimized for creating custom error messages in `.withConstraint` and `.withParser` and includes built-in handling for circular references and string length truncation. ### Usage ```typescript import * as ft from "funtypes"; const result = ft.showValue({ id: 42, name: "Forbes Lindesay" }); // Returns: '{ id: 42, name: "Forbes Lindesay" }' ``` ``` -------------------------------- ### ft.toJsonSchema(codec, options?) Source: https://funtypes.dev/docs/standard-schema Converts a funtypes codec into a JSON Schema object. The options parameter allows specifying the mode (parsed or serialized) and controlling named references. ```APIDOC ## ft.toJsonSchema(codec, options?) ### Description Converts a funtypes codec into a plain JSON Schema object. By default, it describes the parsed shape of the data. ### Parameters - **codec** (Codec) - Required - The funtypes codec to convert. - **options** (Object) - Optional - Configuration options: - **mode** (string) - Optional - 'parsed' (default) or 'serialized' to describe the pre-parse/wire shape. - **namedRefs** (boolean) - Optional - Whether to use named references in the schema. ### Example ```typescript import * as ft from "funtypes"; const UserCodec = ft.Object({ id: ft.Number, name: ft.String, }); const schema = ft.toJsonSchema(UserCodec); ``` ``` -------------------------------- ### ft.ReadonlyRecord(keyCodec, valueCodec) Source: https://funtypes.dev/docs/types-record Creates a codec that validates an object with the same runtime behavior as ft.Record, but produces a readonly object type. ```APIDOC ## ft.ReadonlyRecord(keyCodec, valueCodec) ### Description Validates that an input is an object where all keys conform to the `keyCodec` and all values conform to the `valueCodec`, returning a readonly object. ### Parameters - **keyCodec** (Codec) - Required - A codec for the object keys. - **valueCodec** (Codec) - Required - A codec for the object values. ### Example ```typescript import * as ft from "funtypes"; const MyRecordCodec = ft.ReadonlyRecord(ft.String, ft.Number); ``` ``` -------------------------------- ### Migrate Legacy Data with Funtypes Source: https://funtypes.dev/ Uses a Union codec with a custom parser to transform legacy data structures into modern formats during parsing. ```typescript import * as ft from "funtypes"; const LegacyUserCodec = ft.Object({ id: ft.Number, }); const ModernUserCodec = ft.Object({ id: ft.Number, name: ft.String, }); const UserCodec = ft.Union( ModernUserCodec, // If the data doesn't match the ModernUserCodec, // Funtypes will try matching it against the // LegacyUserCodec instead, and if it's successful, // Funtypes will transform the value using the // `parse` function we provide here. LegacyUserCodec.withParser({ parse(user) { return { success: true, value: { ...user, name: "Anonymous" }, }; }, }), ); // => Codec<{ id: number; name: string }> // ✅ The legacy user will be migrated by adding a // default value for the new "name" property. assert.deepEqual( UserCodec.parse({ id: 42, }), { id: 42, name: "Anonymous", }, ); ``` -------------------------------- ### ft.WithComment(metadata, codec) Source: https://funtypes.dev/docs/types-with-comment Attaches a title and optional description to a Codec for use in JSON Schema generation. ```APIDOC ## ft.WithComment(metadata, codec) ### Description Attaches metadata (title and description) to a funtypes Codec. This metadata is purely for documentation purposes and is picked up when converting the Codec to a JSON Schema. ### Parameters - **metadata** (object) - Required - An object containing a `title` (string) and an optional `description` (string). - **codec** (Codec) - Required - The funtypes Codec to which the metadata should be attached. ### Usage Example ```typescript import * as ft from "funtypes"; const UserCodec = ft.Object({ id: ft.Number, name: ft.WithComment( { title: "Full name", description: "The user's full, legal name", }, ft.String, ), }); ``` ``` -------------------------------- ### Validate simple string-keyed records Source: https://funtypes.dev/docs/types-record Use ft.Record to define a codec where keys are strings and values are numbers. ```typescript import * as ft from "funtypes"; export const MyRecordCodec = ft.Record( ft.String, ft.Number, ); // => ft.Codec<{ [key in string]?: number }> export type MyRecord = ft.Static< typeof MyRecordCodec >; // => { [key in string]?: number } // ✅ Valid Record assert.deepEqual( MyRecordCodec.parse({ "Forty Two": 42 }), { "Forty Two": 42 }, ); // ✅ Valid Empty Record assert.deepEqual(MyRecordCodec.parse({}), {}); // 🚨 Value doesn't match `ft.Number` codec assert.throws(() => MyRecordCodec.parse({ "42": "Forty Two" }), ); // 🚨 Array rather than object assert.throws(() => MyRecordCodec.parse([])); ``` -------------------------------- ### Pick properties from a named object codec Source: https://funtypes.dev/docs/types-pick Shows how ft.Pick preserves the original type name when applied to a codec wrapped in ft.Named. ```typescript import * as ft from "funtypes"; const UserCodec = ft.Named( "User", ft.Object({ id: ft.Number, name: ft.String, }), ); // => Codec<{ id: number; name: string }> const NamedCodec = ft.Pick(UserCodec, ["name"]); // => Codec<{ name: string }> assert.deepEqual( ft.showType(NamedCodec), `Pick`, ); ``` -------------------------------- ### ft.Array(codec) Source: https://funtypes.dev/docs/types-array Creates a codec that validates an input is an array of values matching the provided sub-codec. ```APIDOC ## ft.Array(codec) ### Description Validates that an unknown value is an array where every element satisfies the provided sub-codec. ### Signature `ft.Array(codec: Codec): Codec` ### Parameters - **codec** (Codec) - Required - The codec used to validate each element within the array. ### Usage Example ```typescript import * as ft from "funtypes"; const MyArrayCodec = ft.Array(ft.Number); // Validates array of numbers const result = MyArrayCodec.parse([1, 2, 3]); ``` ``` -------------------------------- ### Define a Buffer codec with ft.Unknown.withConstraint Source: https://funtypes.dev/docs/types-unknown Creates a codec that validates if a value is a node.js Buffer. ```typescript const BufferCodec = ft.Unknown.withConstraint( (value) => Buffer.isBuffer(value) || "Expected a node.js Buffer", { name: "Buffer" }, ); // => ft.Codec assert.deepEqual( BufferCodec.parse(Buffer.from("Hello World")), Buffer.from("Hello World"), ); assert.deepEqual( BufferCodec.safeParse("Hello World"), { success: false, message: "Expected a node.js Buffer", }, ); ``` -------------------------------- ### ft.InstanceOf(Class) Source: https://funtypes.dev/docs/types-instanceof Validates that a value is an instance of the provided class constructor. ```APIDOC ## ft.InstanceOf(Class) ### Description Validates that an unknown value is an instance of a specific class. This validator ensures the value maintains its prototype chain and is not just a plain object with matching properties. ### Usage ```typescript import * as ft from "funtypes"; export class User { public readonly name: string; constructor(name: string) { this.name = name; } } const UserCodec = ft.InstanceOf(User); // Returns the instance if valid const user = UserCodec.parse(new User("Name")); ``` ### Important Note Do not use `ft.InstanceOf` within `ft.Intersect` when combined with `ft.Object` codecs, as `ft.Object` creates a new object instance which breaks the prototype check required by `ft.InstanceOf`. ``` -------------------------------- ### ft.KeyOf Source: https://funtypes.dev/docs/types-keyof Creates a codec that validates if a value is a key of the provided object. For objects with numeric keys, it accepts both the number and its string representation. ```APIDOC ## ft.KeyOf(object) ### Description Validates that an unknown value is a key of the provided object. Returns a Codec that matches the union of the object's keys. ### Usage ```typescript import * as ft from "funtypes"; const UserKeyCodec = ft.KeyOf({ id: {}, name: {}, }); // Validates input const result = UserKeyCodec.parse("name"); ``` ### Parameters - **object** (Object) - Required - An object whose keys define the valid set of values for the codec. ### Behavior - **String Keys**: Validates that the input matches one of the object's string keys. - **Numeric Keys**: Accepts both the numeric value and its string representation as valid inputs. ``` -------------------------------- ### Attach only a title Source: https://funtypes.dev/docs/types-with-comment The description field is optional when using WithComment. ```typescript import * as ft from "funtypes"; const ScoreCodec = ft.WithComment( { title: "Score" }, ft.Number, ); assert.deepEqual(ft.toJsonSchema(ScoreCodec), { type: "number", title: "Score", }); ``` -------------------------------- ### Behavior of test and serialize with defaults Source: https://funtypes.dev/docs/types-with-default Explains that test and serialize do not apply defaults, as they operate on already-parsed values. ```typescript import * as ft from "funtypes"; const RoleCodec = ft.WithDefault( ft.String, "member", ); assert.deepEqual(RoleCodec.test(undefined), false); assert.throws(() => RoleCodec.serialize(undefined as any)); ``` -------------------------------- ### ft.Omit(codec, keys) Source: https://funtypes.dev/docs/types-omit Creates a new Codec by excluding specified keys from the provided object Codec. ```APIDOC ## ft.Omit(codec, keys) ### Description Excludes specific properties from an existing object Codec. This utility is useful for creating derived object schemas. ### Signature `ft.Omit(codec: Codec, keys: string[]): Codec>` ### Parameters - **codec** (Codec) - Required - The source object Codec to omit properties from. - **keys** (string[]) - Required - An array of property names to exclude from the resulting Codec. ### Example ```typescript import * as ft from "funtypes"; const UserCodec = ft.Object({ id: ft.Number, name: ft.String, }); const NamedCodec = ft.Omit(UserCodec, ["id"]); // Result: Codec<{ name: string }> ``` ``` -------------------------------- ### Define a Never codec Source: https://funtypes.dev/docs/types-never Use ft.Never to create a codec that always throws an error during parsing. ```typescript import * as ft from "funtypes"; const MyCodec = ft.Never; // => ft.Codec type MyType = ft.Static; // => never // 🚨 Never will always throw when you try to // parse something assert.throws(() => MyCodec.parse({})); ``` -------------------------------- ### Using Codec.test for TypeScript type narrowing Source: https://funtypes.dev/docs/funtypes-api-test Shows how Codec.test can be used within conditional logic to narrow types for TypeScript inference. ```typescript /** * TypeScript can infer that this function returns * a value of type `string`. */ function safelyGetUserName(user: unknown) { if (UserCodec.test(user)) { return user.name; } else { return "Unknown user"; } } // ✅ Valid so no error is thrown and the name // is returned assert.deepEqual( safelyGetUserName({ id: 1, name: "Forbes Lindesay", dateOfBirth: new Date( "1970-01-01T00:00:00.000Z", ), }), "Forbes Lindesay", ); // 🚨 Invalid: even though this object has a // "name" prop, It is not a valid User object, // so UserCodec.test will return false. assert.deepEqual( safelyGetUserName({ name: "Forbes Lindesay", }), "Unknown user", ); ```