Skip to content
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
7 changes: 7 additions & 0 deletions etc/types.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export abstract class BaseTypeImpl<ResultType, TypeConfig = unknown> implements
withBrand<const BrandName extends string>(name: BrandName): Type<Branded<ResultType, BrandName>, TypeConfig>;
withConfig<const BrandName extends string>(name: BrandName, newConfig: TypeConfig): Type<Branded<ResultType, BrandName>, TypeConfig>;
withConstraint<const BrandName extends string>(name: BrandName, constraint: Validator<ResultType>): Type<Branded<ResultType, BrandName>, TypeConfig>;
withDefault(...args: [value: DeepUnbranded<ResultType>] | [name: string, value: DeepUnbranded<ResultType>] | [options: WithDefaultOptions, value: DeepUnbranded<ResultType>]): this;
withName(name: string): this;
withParser(...args: [newConstructor: (i: unknown) => unknown] | [name: string, newConstructor: (i: unknown) => unknown] | [options: ParserOptions, newConstructor: (i: unknown) => unknown]): this;
withValidation(validation: Validator<ResultType>): this;
Expand Down Expand Up @@ -696,6 +697,12 @@ export type WithBrands<T, BrandNames extends string> = T & {
};
};

// @public
export interface WithDefaultOptions {
clone?: boolean;
name?: string;
}

// @public
export type Writable<T> = {
-readonly [P in keyof T]: T[P];
Expand Down
1 change: 1 addition & 0 deletions markdown/types.basetypeimpl.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ All type-implementations must extend this base class. Use [createType()](./types
| [withBrand(name)](./types.basetypeimpl.withbrand.md) | | Create a new instance of this Type with the given name. |
| [withConfig(name, newConfig)](./types.basetypeimpl.withconfig.md) | | Create a new instance of this Type with the additional type-specific config, such as min/max values. |
| [withConstraint(name, constraint)](./types.basetypeimpl.withconstraint.md) | | Create a new type based on the current type and use the given constraint function as validation. |
| [withDefault(args)](./types.basetypeimpl.withdefault.md) | | Define a new type with the same specs, but with the given default value. |
| [withName(name)](./types.basetypeimpl.withname.md) | | Create a new instance of this Type with the given name. |
| [withParser(args)](./types.basetypeimpl.withparser.md) | | Define a new type with the same specs, but with the given parser and an optional new name. |
| [withValidation(validation)](./types.basetypeimpl.withvalidation.md) | | Clone the type with the added validation. |
27 changes: 27 additions & 0 deletions markdown/types.basetypeimpl.withdefault.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@skunkteam/types](./types.md) &gt; [BaseTypeImpl](./types.basetypeimpl.md) &gt; [withDefault](./types.basetypeimpl.withdefault.md)

## BaseTypeImpl.withDefault() method

Define a new type with the same specs, but with the given default value.

**Signature:**

```typescript
withDefault(...args: [value: DeepUnbranded<ResultType>] | [name: string, value: DeepUnbranded<ResultType>] | [options: WithDefaultOptions, value: DeepUnbranded<ResultType>]): this;
```

## Parameters

| Parameter | Type | Description |
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
| args | \[value: [DeepUnbranded](./types.deepunbranded.md)<!-- -->&lt;ResultType&gt;\] \| \[name: string, value: [DeepUnbranded](./types.deepunbranded.md)<!-- -->&lt;ResultType&gt;\] \| \[options: [WithDefaultOptions](./types.withdefaultoptions.md)<!-- -->, value: [DeepUnbranded](./types.deepunbranded.md)<!-- -->&lt;ResultType&gt;\] | |

**Returns:**

this

## Remarks

This is a convenient method that adds a simple parser that resolves to the given `value` whenever the `input` to the parser is `undefined`<!-- -->.
1 change: 1 addition & 0 deletions markdown/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Runtime type-validation with derived TypeScript types.
| [TypeLink](./types.typelink.md) | An object that has an associated TypeScript type. |
| [ValidationOptions](./types.validationoptions.md) | |
| [Visitor](./types.visitor.md) | Interface for a visitor that is accepted by all types (classic visitor-pattern). |
| [WithDefaultOptions](./types.withdefaultoptions.md) | Options that can be passed to [BaseTypeImpl.withDefault()](./types.basetypeimpl.withdefault.md)<!-- -->. |

## Variables

Expand Down
17 changes: 17 additions & 0 deletions markdown/types.withdefaultoptions.clone.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@skunkteam/types](./types.md) &gt; [WithDefaultOptions](./types.withdefaultoptions.md) &gt; [clone](./types.withdefaultoptions.clone.md)

## WithDefaultOptions.clone property

Whether to clone the given value on each use.

**Signature:**

```typescript
clone?: boolean;
```

## Remarks

When `true` (the default), on each use the value will be cloned using `structuredClone` to prevent subtle bugs because of object reuse.
20 changes: 20 additions & 0 deletions markdown/types.withdefaultoptions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@skunkteam/types](./types.md) &gt; [WithDefaultOptions](./types.withdefaultoptions.md)

## WithDefaultOptions interface

Options that can be passed to [BaseTypeImpl.withDefault()](./types.basetypeimpl.withdefault.md)<!-- -->.

**Signature:**

```typescript
interface WithDefaultOptions
```

## Properties

| Property | Modifiers | Type | Description |
| --------------------------------------------- | --------- | ------- | ---------------------------------------------------------- |
| [clone?](./types.withdefaultoptions.clone.md) | | boolean | _(Optional)_ Whether to clone the given value on each use. |
| [name?](./types.withdefaultoptions.name.md) | | string | _(Optional)_ The new name to use in error messages. |
13 changes: 13 additions & 0 deletions markdown/types.withdefaultoptions.name.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@skunkteam/types](./types.md) &gt; [WithDefaultOptions](./types.withdefaultoptions.md) &gt; [name](./types.withdefaultoptions.name.md)

## WithDefaultOptions.name property

The new name to use in error messages.

**Signature:**

```typescript
name?: string;
```
18 changes: 18 additions & 0 deletions src/base-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import type {
ValidationResult,
Validator,
Visitor,
WithDefaultOptions,
} from './interfaces';
import { designType } from './symbols';
import {
Expand Down Expand Up @@ -358,6 +359,23 @@ export abstract class BaseTypeImpl<ResultType, TypeConfig = unknown> implements
return type;
}

/**
* Define a new type with the same specs, but with the given default value.
*
* @remarks
* This is a convenient method that adds a simple parser that resolves to the given `value` whenever the `input` to the parser is `undefined`.
*/
withDefault(
...args:
| [value: DeepUnbranded<ResultType>]
| [name: string, value: DeepUnbranded<ResultType>]
| [options: WithDefaultOptions, value: DeepUnbranded<ResultType>]
): this {
const [{ name, clone = true }, value] = decodeOptionalOptions<WithDefaultOptions, DeepUnbranded<ResultType>>(args);
const cloneFn = clone ? structuredClone : <T>(i: T) => i;
return this.withParser({ name, chain: true }, input => (input === undefined ? cloneFn(value) : input));
}

/**
* Clone the type with the added validation.
*
Expand Down
20 changes: 20 additions & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,26 @@ export interface ParserOptions {
chain?: boolean;
}

/**
* Options that can be passed to {@link BaseTypeImpl.withDefault}.
*/
export interface WithDefaultOptions {
/**
* The new name to use in error messages.
*/
name?: string;
/**
* Whether to clone the given value on each use.
*
* @remarks
* When `true` (the default), on each use the value will be cloned using `structuredClone` to prevent subtle bugs because of object
* reuse.
*
* @defaultValue `true`
*/
clone?: boolean;
}

/** The supported types of literals. */
export type LiteralValue = string | number | boolean | null | undefined | void;

Expand Down
Loading