If you know how to fix the issue, make a pull request instead.
@types/yup package and had problems.Definitions by: in index.d.ts) so they can respond.If you do not mention the authors the issue will be ignored.
Describe the bug
When when an object schema is created, it's Typescript types are faithfully inferred via InferType, but when that same schema is included as a nested schema within another object, the resulting inferred types incorrectly drop the optional status of any nested fields.
To Reproduce
Minimal repro here:
https://codesandbox.io/s/yup-infertype-nested-optional-issue-v63dd
import { object, string, InferType } from "yup";
const nameSchema = object({
first: string().notRequired(),
last: string().notRequired()
});
type NameType = InferType<typeof nameSchema>;
const userSchema = object({
name: nameSchema.notRequired()
});
type UserType = InferType<typeof userSchema>;
class User implements UserType {
name?: NameType;
}
Results in error:
Property 'name' in type 'User' is not assignable to the same property in base type 'Id<Partial<Pick<{ name: { first: ...; last: ...; }; }, "name">> & Pick<{ name: { first: ...; last: ...; }; }, never>>'.
Type 'Id<Partial<Pick<{ first: string; last: string; }, "first" | "last">> & Pick<{ first: string; last: string; }, never>>' is not assignable to type '{ first: string; last: string; }'.
Property 'first' is optional in type 'Id<Partial<Pick<{ first: string; last: string; }, "first" | "last">> & Pick<{ first: string; last: string; }, never>>' but required in type '{ first: string; last: string; }'.ts(2416)
Based on the hints, NameType by itself is correct:
type NameType = {
first?: string;
last?: string;
}
but once included in UserType, the optionals get dropped:
type UserType = {
name?: {
first: string;
last: string;
};
}
Expected behavior
Optional state of fields is preserved for nested schemas
Here's a more minimal repro (https://codesandbox.io/s/yup-infertype-nested-optional-issue-mgnhg):
const exampleSchema = object({
b: object({
a: number()
})
});
const a: InferType<typeof exampleSchema> = {
b: {
}
};
Oddly though, I can only reproduce in your sandbox, not on my machine. It's possible that this has something to do with TypeScript versions, as I'm using 3.9.3 on my machine and the sandbox is is on 3.8.3. I can't play with it more at the moment though.
Thanks for the _much_ better repro and for looking into it. I was running 3.8.3 on my machine but have since upgraded and it still repros:

OK, I think I've tracked down the difference between our setups. You need to enable strict mode by creating in your project a tsconfig.json file with:
{
"compilerOptions": {
"strict": true
}
}
If you do this, then the type inference will work. Otherwise TypeScript considers undefined to be assignable to anything so it ignores it, so (for example) number().notRequired() gives us NumberSchema<number> instead of NumberSchema<number | undefined>. The undefined part is discarded. Then because the schema type doesn't extend undefined, it's not marked as optional by the utility types. See also https://github.com/jquense/yup#typescript-setting.
The minimal bug still reproduces for me even with the correct tsconfig.json
~/typesyupbug $ npx tsc --showConfig
{
"compilerOptions": {
"strict": true,
"strictNullChecks": true
},
"files": [
"./test.ts"
]
}
~/typesyupbug $ npx tsc test.ts
test.ts:10:3 - error TS2741: Property 'a' is missing in type '{}' but required in type '{ a: number; }'.
10 b: {
~
test.ts:5:7
5 a: number()
~~~~~~~~~~~
'a' is declared here.
test.ts:4:3
4 b: object({
~~~~~~~~~~~
5 a: number()
~~~~~~~~~~~~~~~~~
6 })
~~~~
The expected type comes from property 'b' which is declared here on type 'Id<Partial<Pick<{ b: { a: ...; }; }, "b">> & Pick<{ b: { a: ...; }; }, never>>'
Found 1 error.
~/typesyupbug $ npx tsc --version
Version 3.9.3
This is also an issue for me, would appreciate any support on it
This is also an issue for me, would appreciate any support on it
Make sure you're using strictnullchecks please. You can test that by seeing if this errors:
const x: number = null;
Make sure you're using strictnullchecks please.
As you can see in my comment back in May, enabling strictnullchecks was not sufficient to fix this bug. I just ran it again to verify. I'm using the options to specified and the minimal repro you wrote. Modules are up to date
Agreed, similar to @anstosa I have tsconfig.json
"strictNullChecks": true,
"strict": true
Also, the original repro @anstosa provided works correctly for me: https://codesandbox.io/s/yup-infertype-nested-optional-issue-v63dd
However the one @iansan5653 provided does not: https://codesandbox.io/s/yup-infertype-nested-optional-issue-mgnhg
I have forked your repro to get it working, it was missing @types/yup in the package.json, so all your types were resolving to any. The fixed repro is available here: https://codesandbox.io/s/yup-infertype-nested-optional-issue-90sgo
If you hover over line 12's b you'll see this:

This means that key a needs to exist but can have an undefined value, what we would prefer is for key a not to be required at all.
Our current workaround is to Omit<T, 'a'> & { a?: number;}
I believe I have resolved this issue and have raised a PR for it here: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/46518
Most helpful comment
I believe I have resolved this issue and have raised a PR for it here: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/46518