Yup: Cannot omit optional parameter

Created on 6 Jan 2021  路  1Comment  路  Source: jquense/yup

Describe the bug
When I define an object with optional parameters, I expect the type inferred from the object to allow missing fields.

To Reproduce

import * as yup from 'yup';
import Axios from "axios";

const idSchema = yup.object({
    id: yup.string(),
});

type IdSchema = yup.TypeOf<typeof idSchema>;

interface Id2 {
    id?: string;    
} 

const id1: IdSchema = {};   // Property 'id' is missing in type '{}' but required in type 'TypeOfShape<{ id: StringSchema<string | undefined, Record<string, any>, string | undefined>; }>'
const id2: Id2 = {}; // OK
const id3: IdSchema = { id: undefined };  // OK

Expected behavior
allow missing fields. I don't know if this is expected, but if you have the majority fields of a schema to be optional, it is very verbose to explicitly note them to be undefined

Platform (please complete the following information):

  • Browser [e.g. chrome, safari] node
  • Version [e.g. 22] yup 0.32.8, typescript 4.1.2

Additional context
Add any other context about the problem here.

Most helpful comment

I wrote my own type inference compatible with yup 0.32.8:

import * as yup from 'yup';
import { ObjectSchema } from "yup";
import { ObjectShape } from "yup/lib/object";
import { TypedSchema } from "yup/lib/util/types";

export type InferShape<TSchema> =
    TSchema extends ObjectSchema<infer Shape> ? Shape : never;

export type UndefinableKeys<Shape extends ObjectShape> = string & {
    [K in keyof Shape]?:
    Shape[K] extends TypedSchema ?
    undefined extends yup.InferType<Shape[K]> ?
    K : never
    : never;
}[keyof Shape];

export type InferInterfaceFromShape<Shape extends ObjectShape> = {
    [K in UndefinableKeys<Shape>]?: Shape[K] extends TypedSchema ? yup.InferType<Shape[K]> : any;
} & {
        [K in Exclude<keyof Shape, UndefinableKeys<Shape>>]: Shape[K] extends TypedSchema ? yup.InferType<Shape[K]> : any;
    }

export type InferInterface<TSchema> =
    InferInterfaceFromShape<InferShape<TSchema>>;

Now you can omit key in your value against the inferred type:

const person = yup.object({
    firstName: yup.string(),
    lastName: yup.string().defined(),
});

type Shape = InferShape<typeof idSchema2>;

type Interface = InferInterfaceFromShape<Shape>;

const t: Interface = {
    // firstName is optional and omitted
    lastName: "abramov",
};

>All comments

I wrote my own type inference compatible with yup 0.32.8:

import * as yup from 'yup';
import { ObjectSchema } from "yup";
import { ObjectShape } from "yup/lib/object";
import { TypedSchema } from "yup/lib/util/types";

export type InferShape<TSchema> =
    TSchema extends ObjectSchema<infer Shape> ? Shape : never;

export type UndefinableKeys<Shape extends ObjectShape> = string & {
    [K in keyof Shape]?:
    Shape[K] extends TypedSchema ?
    undefined extends yup.InferType<Shape[K]> ?
    K : never
    : never;
}[keyof Shape];

export type InferInterfaceFromShape<Shape extends ObjectShape> = {
    [K in UndefinableKeys<Shape>]?: Shape[K] extends TypedSchema ? yup.InferType<Shape[K]> : any;
} & {
        [K in Exclude<keyof Shape, UndefinableKeys<Shape>>]: Shape[K] extends TypedSchema ? yup.InferType<Shape[K]> : any;
    }

export type InferInterface<TSchema> =
    InferInterfaceFromShape<InferShape<TSchema>>;

Now you can omit key in your value against the inferred type:

const person = yup.object({
    firstName: yup.string(),
    lastName: yup.string().defined(),
});

type Shape = InferShape<typeof idSchema2>;

type Interface = InferInterfaceFromShape<Shape>;

const t: Interface = {
    // firstName is optional and omitted
    lastName: "abramov",
};
Was this page helpful?
0 / 5 - 0 ratings