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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Usable input types for validation #660

Open
mjburghoffer opened this issue Sep 2, 2022 · 0 comments
Open

Usable input types for validation #660

mjburghoffer opened this issue Sep 2, 2022 · 0 comments

Comments

@mjburghoffer
Copy link

馃殌 Feature request

Current Behavior

The current behavior of Validate<I, A> is to effectively make I = unknown 100% of the time. Consider the following example

import { Temporal } from '@js-temporal/polyfill';
import * as t from 'io-ts';

const TemporalInstant = new t.Type<Temporal.Instant, string, bigint | string | Temporal.Instant>(
  'Instant',
  (v): v is Temporal.Instant => v instanceof Temporal.Instant,
  (v: unknown, c) => {
    // the parameter v, here, should be unknown (instead of I, aka bigint | string | Temporal.Instant)
    try {
      const result = new Temporal.Instant(v);
      return t.success(result);
    } catch (e) {
      return t.failure(v, c);
    }
  },
  (v) => v.toString(),
);

const Widget = t.type({
  name: t.string,
  /*
  TS2322: Type 'Type<Instant, string, string | bigint | Instant>' is not assignable to type 'Mixed'. 聽聽Types of property 'validate' are incompatible.
 聽聽聽聽Type 'Validate<string | bigint | Instant, Instant>' is not assignable to type 'Validate<unknown, any>'.
 聽聽聽聽聽聽Type 'unknown' is not assignable to type 'string | bigint | Instant'.
  */
  createdAt: TemporalInstant,
});

Desired Behavior

Understandibly the incoming data is always unknown - so in a way, that doesn't actually need to be a generic type.

What is important is signaling to consumers of the type what types of inputs are expected. Put another way, InputOf<T> could be used as a type that consumers could actually make use of.

// given
type WidgetInput = t.InputOf<typeof Widget>;

// what actually happens
type CurrentBehavior = unknown;

// what could happen in a perfect world
type ExpectedBehavior = {
  name: string,
  createdAt: string | bigint | Temporal.Instant,
}

Suggested Solution

This might be as simple as changing the interface for Decoder:

export interface Decoder<A> {
  readonly name: string
  readonly validate: Validate<unknown, A>
  readonly decode: Decode<unknown, A>
}

Since I = unknown is the only case that exists, this shouldn't have much of an effect on the behavior of anything. Then it would be necessary to modify all of the existing types to make use of the new generic arguments, ie:

export class StringType extends Type<string> {}

Should be changed to

export class StringType extends Type<string, string, string> {}

Who does this impact? Who is this for?

Anyone who wants to use InputType<T> to model valid inputs for consumers of API's backed by io-ts.

Describe alternatives you've considered

Using covariance and contravariance hacks, subclassing Type in my own project.

Your environment

Software Version(s)
io-ts 2.2.17
fp-ts
TypeScript 4.7.2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant