2.1.0 breaks TS build
mmouterde opened this issue ยท 21 comments
2.0.0 works great but once updated to 2.1.0 I got strange errors from ts compiler.
Type '{ type: string; required: string[]; properties: { firstName: { type: string; }; lastName: { type: string; }; email: { type: string; }; phone: { type: string[]; }; password: { type: string; }; participantId: { type: string; }; }; }' is not assignable to type 'ValidateFunction'.
Type '{ type: string; required: string[]; properties: { firstName: { type: string; }; lastName: { type: string; }; email: { type: string; }; phone: { type: string[]; }; password: { type: string; }; participantId: { type: string; }; }; }' is not assignable to type 'JSONSchema7'.
Types of property 'type' are incompatible.
Type 'string' is not assignable to type '"string" | "number" | "boolean" | "object" | "integer" | "null" | "array" | JSONSchema7TypeName[]'.
I will look into it ASAP.
Can you please provide a minimal reproduction repo or example?
sure https://github.com/mmouterde/bugDemo
import { Validator } from 'express-json-validator-middleware';
const UserParamSchemaForUpdate = {
type: 'object',
properties: {
firstName: { type: 'string' }
},
};
const validator = new Validator({
allErrors: true,
format: 'full',
jsonPointers: true,
});
validator.validate({
body: UserParamSchemaForUpdate,
});
Basically body
is not compatible with objects unless explicitly specified such as:
app.post('/template', validate({ body: schemas.templateCreate as ValidateFunction }), templateController.create_template);
@vacekj I'm having the same problem.
@rmolinamir is correct in that the error is triggered by type: "object"
. His solution doesn't make any sense to me, unfortunately.
The strange thing is that passing an object directly to the validate
function works. For example:
import { Validator } from 'express-json-validator-middleware'
const validator = new Validator({ allErrors: true })
const mySchema = { type: 'object', properties: {} }
// Passing the schema inline works
validator.validate({ body: { type: 'object', properties: {} } })
// Passing the variable throws an error
validator.validate({ body: mySchema })
// Even expanding the variable throws an error
validator.validate({ body: { ...mySchema } })
I'm completely stumped. Any thoughts?
Update
The following functions as a workaround.
import { JSONSchema7 } from 'json-schema'
validator.validate({ body: mySchema as JSONSchema7 })
I experienced the same issue, what worked for me was to put as const
after the type value, e.g.
{
type: 'object' as const
...
}
unfortunately this meant not being able to use .json files anymore, had to migrate them to TS
I think the advantage of this approach is that TS will complain when a invalid value for type is entered, which it won't do when explicit type casting to JSONSchema7.
Hi folks ๐ I'm the new maintainer of this project.
I've just started looking into this issue and will attempt to reproduce it. I'll let you know how I get on, but any further insights as to what the issue might be are very welcome in the meantime!
It looks like this issue was actually introduced by adding types, period - 2.1.0
was the first package version to actually have type definitions, and they've changed little since then.
@NGTOne That makes sense. I think this issue needs to be resolved as removing the types completely isn't a viable option. I'm hoping to have time to do some digging on this later this week.
I've been trying to resolve this issue, but it's proving tricky. I've created a minimal reproducible example demonstrating the problem: https://bit.ly/3oIwQ9z (thanks to @monooso for sharing their workaround above).
@simonplend You need to use as const
to make object
a string literal:
It works good here: https://tsplay.dev/WJ96lm
@monooso can you provide a minimal working example in CodeSandbox with json?
However, as I understand, even with "resolveJsonModule": true
you will get string
, but not the string literal
But you can convert *.json
to *.ts
files with as const
and it will work just fine for you
@Beraliv That's great, thank you! It seems similar to the solution which @senorpedro mentioned above.
This raises a couple of questions for me:
- Why does making
object
a string literal fix things? - Is it preferable to put
as const
after theobject
string, or after the whole object itself?
I really appreciate everyone's help with this issue. I only have a little experience with TypeScript, and I want to make sure I resolve this issue correctly, even if it just means adding a section to the README with some guidance on using this library with TypeScript.
Why does making
object
a string literal fix things?
I checked the validate
function implementation and found string literals there:
- Open https://github.com/simonplend/express-json-validator-middleware/blob/main/src/index.d.ts#L27
- It uses
ValidatorFunction
โ https://github.com/simonplend/express-json-validator-middleware/blob/main/src/index.d.ts#L18-L20 - Inside it uses
AllowedSchema
โ https://github.com/simonplend/express-json-validator-middleware/blob/main/src/index.d.ts#L13-L16 - Inside
JSONSchema7
you will findtype
โ https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/json-schema/index.d.ts#L627 - This type should be a string literal โ https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/json-schema/index.d.ts#L570-L577
Check out the example here: https://tsplay.dev/wee2Ew
- If you inline the code, it will infer string as a string literal (and you will have no error)
- If you use
as const
either for object or for a string, it will convert the string to string literal and you will have no error too
You need as const
because of the way how you declared the type:
type ValidateParameters = { type: 'object', parameters: Record<string, unknown> } | { type: 'string'; parameters: string; };
If you use 'object'
in type
or interface
, it means you use string literals and you require to pass it from outside the same way (as string literal)
Is it preferable to put
as const
after theobject
string, or after the whole object itself?
It you call as const
on the whole object, all strings inside will become string literals. But if you have at least one string literal inside you schema (e.g. JSONSchema7
), as const
for the whole object will be helpful and easy way to fix the problem:
- If you have
string
in types, both string literal and string can be passed - If you have string literal in types, only this string literal can be passed
You can check examples here: https://tsplay.dev/wj53YW
@Beraliv Thank you so much for this detailed detective work, the thorough explanation, and for all of the examples too. They are really helpful.
I think I understand everything you've explained, but I'm going to read it all again with fresh eyes tomorrow.
I'm planning to add a short section to the project README with tips on usage with TypeScript. If you are happy to, I would really appreciate you looking over these documentation changes before I merge them. Would you mind me tagging you in the PR?
Would you mind me tagging you in the PR?
Not at all, I'm fine with it ๐
@simonplend do you still need help?
I was looking at the documentation for the json-schema-to-ts package recently. I noticed their documentation also mentions the need to use as const
when defining JSON schemas in TypeScript: https://www.npmjs.com/package/json-schema-to-ts#fromschema
I'm planning to merge #89 tomorrow so that I can close this issue and get v2.2.0 released.
Thanks for your help everyone!
Adding as const
did not fix the errors for me.
My error:
Error:(30, 14) TS2322: Type '{ type: string; properties: { mustHaveField: { type: string; }; }; required: string[]; }' is not assignable to type 'ValidateFunction'.
Type '{ type: string; properties: { mustHaveField: { type: string; }; }; required: string[]; }' is not assignable to type 'JSONSchema7'.
Types of property 'type' are incompatible.
Type 'string' is not assignable to type 'JSONSchema7TypeName | JSONSchema7TypeName[]'.
Solution:
const schema: JSONSchema7 = {
type: 'object',
properties: {
mustHaveField: { type: 'boolean' },
},
required: ['mustHaveField'],
};
Also updated to the latest json-schema type definition to avoid type mismatch error. At time of writing that's "@types/json-schema": "^7.0.9",
HTH