You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Enforcing types to be narrowed from generic types #26332
enforce narrowing, narrow types, narrow, force narrow type
Suggestion
First of all, sorry if there is already a way to do this, I've been reading thru the documentation, searched thru the issues, and crawled the web in order to find anything, but haven't so far.
I would like a way to force narrow types to be passed to my function, and specifically disallow the generic versions (e.g. string, number, boolean).
What I want is for my function to accept any subtype of e.g. string, but not string itself. So both 'a' | 'b', 'a', and 'hello' | 'world' | '!' would be accepted, but not string.
Use Cases
I think that this is one of the most illustrating use cases:
Basically, I want to have a type that maps a JSON Schema into a TypeScript type. I then provide a function with a return value of input is TheGeneratedType.
Currently, the enum values need to be typed with 'test' as 'test' in order not to be generalized as just string:
// Current behavior:functionfoobar<{enum: [1,2,3]}>(input: any): input is number// Wanted behavior:functionfoobar<{enum: [1,2,3]}>(input: any): inputis1|2|3
Which would basically select the variant: foobar<1, 2>(...)
Andy (Andrewkraft) (@Andy-MS) if you feel like it, I would love some quick eyes on these definitions, it seems like I might be missing some smart trick to fix the nested require types 🤔 ❤️
The goal with the package is to be able to infer the TypeScript interface from the JSON Schema, such as to avoid having to type it twice.
I wasn't able to get TypeScript to infer even the simplest schema when going the other way around:
declaretypeSchemaFor<T>=Textendsnull ? {type: "null"}
: Textendsboolean ? {type: "boolean"}
: Textendsnumber ? {type: "number"}
: Textendsstring ? {type: "string"}
: neverdeclarefunctioncreateValidator<T>(schema: SchemaFor<T>): any// This errors with: Argument of type '{ type: string; }' is not assignable to parameter of type 'never'.createValidator({type: 'string'})
One huge thing when writing an API is dealing with input validation, and when using TypeScript I almost always end up typing everything twice; once in JSON Schema, and then in TypeScript as well in order to get proper typings.
This is very tedious and also leads to hard to spot mistakes, e.g. I small mismatch between the JSON Schema and the TypeScript interface can make me think that everything is working, but when I deploy the API it will actually reject the input I intended.
I really appreciate you taking the time to respond to me 💌
Oh, and we can fix this by adding an overload specifically for an object schema, where the object schema is the root:
+declare function createValidator<PropertiesSchemas extends Record<string, AnySchema>, RequiredFields extends keyof PropertiesSchemas>(schema: ObjectSchema<PropertiesSchemas, RequiredFields>): TypeFromSchema<ObjectSchema<PropertiesSchemas, RequiredFields>> // Return TypeFromSchema<S> to make it easy to test
declare function createValidator<S extends AnySchema>(schema: S): TypeFromSchema<S> // Return TypeFromSchema<S> to make it easy to test
I'm having a bit of a hard time grasping exactly how existential types works, even after reading the other thread twice 😆
But from what I gathered it would allow me to express this:
That would also allow you to express relations between types, such as that required must be a subset of the property keys.
Which is awesome!
I'll guess I'll have to go with a ton of overloads for now, but it would be really really nice to see existential types, and variadic kinds get into TypeScript ☺️
Still, I'm not sure that that would fix my initial problem with the enum type? I would still need a way to express that the enum type cannot be a generic type, and have to be a narrow type?
interfaceEnumSchema<T>{enum: T[]}declarefunctioncreateValidator<T>(schema: EnumSchema<T>): T[]// This would give `number[]` instead of `(1 | 2 | 3)[]`createValidator({enum: [1,2,3]})// This would work thoughcreateValidator({enum: [1as1,2as2,3as3]})
I don't have a perfect proposal for how this could be solved though 🤔
Although my express goal right now is to provide typings for the package is-my-json-valid which already have a ton of downloads. I want to provide out-of-the-box awesome experience when using VS Code and is-my-json-valid 🙌
This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow.
TLDR; You probably can make use of the new <const> which will narrow a string such as 'Apple' to the type Apple. It's still a string, but it's now distinct from a Banana.
Activity
LinusU commentedon Aug 9, 2018
I might be missing something, but I cannot get that to work with an array 🤔
But yeah, unfortunately, my main concern is for the consumers of is-my-json-valid not to have to type
'a' as 'a'...Maybe this is the wrong approach though 🤔
The interesting thing is that I can sort of get it to work on the
requiredfield, but not on nestedrequiredfields. See here: https://github.com/mafintosh/is-my-json-valid/blob/8ef04cc80eff073e4ebbe5af17cbdd877aa4b443/test/typings.ts#L114-L135So possibly this could be solved instead with variadic kinds instead (#5453) 🤔
Something like this:
Which would basically select the variant:
foobar<1, 2>(...)Andy (Andrewkraft) (@Andy-MS) if you feel like it, I would love some quick eyes on these definitions, it seems like I might be missing some smart trick to fix the nested require types 🤔 ❤️
https://github.com/mafintosh/is-my-json-valid/blob/master/index.d.ts
LinusU commentedon Aug 9, 2018
The goal with the package is to be able to infer the TypeScript interface from the JSON Schema, such as to avoid having to type it twice.
I wasn't able to get TypeScript to infer even the simplest schema when going the other way around:
One huge thing when writing an API is dealing with input validation, and when using TypeScript I almost always end up typing everything twice; once in JSON Schema, and then in TypeScript as well in order to get proper typings.
This is very tedious and also leads to hard to spot mistakes, e.g. I small mismatch between the JSON Schema and the TypeScript interface can make me think that everything is working, but when I deploy the API it will actually reject the input I intended.
I really appreciate you taking the time to respond to me 💌
LinusU commentedon Aug 9, 2018
Wow, this is amazing 😍 Looks great 👏
Now I only need to get the
requiredbehavior to work also, but I think this will work!!Thank you so much, this is awesome ❤️
Actually, I've been here before 😂
The problem now is that when we add the
requiredbehavior all the keys will be of typestringinstead of the literal.The failure will then be:
Which can be fixed by:
LinusU commentedon Aug 9, 2018
Oh, and we can fix this by adding an overload specifically for an object schema, where the object schema is the root:
+declare function createValidator<PropertiesSchemas extends Record<string, AnySchema>, RequiredFields extends keyof PropertiesSchemas>(schema: ObjectSchema<PropertiesSchemas, RequiredFields>): TypeFromSchema<ObjectSchema<PropertiesSchemas, RequiredFields>> // Return TypeFromSchema<S> to make it easy to test declare function createValidator<S extends AnySchema>(schema: S): TypeFromSchema<S> // Return TypeFromSchema<S> to make it easy to testBut that leads us to the next problem:
and here I'm at a loss 🤔
LinusU commentedon Aug 10, 2018
Ahh, yeah that makes sense 🤔
I'm having a bit of a hard time grasping exactly how existential types works, even after reading the other thread twice 😆
But from what I gathered it would allow me to express this:
Which is awesome!
I'll guess I'll have to go with a ton of overloads for now, but it would be really really nice to see existential types, and variadic kinds get into TypeScript☺️
Still, I'm not sure that that would fix my initial problem with the
enumtype? I would still need a way to express that the enum type cannot be a generic type, and have to be a narrow type?I don't have a perfect proposal for how this could be solved though 🤔
Maybe just a
narrowkeyword?That would disallow the generic types
string,number,boolean, but I'm not too familiar to say if this would be a good solution 🤔AlCalzone commentedon Aug 10, 2018
If you want to avoid writing code for a schema, maybe you could go with a VSCode extension?
https://marketplace.visualstudio.com/items?itemName=quicktype.quicktype
(or the underlying library: https://github.com/quicktype/quicktype)
I haven't used it yet, but at first glance that looks kinda like what you want to do here.
LinusU commentedon Aug 10, 2018
Seems very cool!
Although my express goal right now is to provide typings for the package
is-my-json-validwhich already have a ton of downloads. I want to provide out-of-the-box awesome experience when using VS Code andis-my-json-valid🙌typescript-bot commentedon Jul 13, 2019
This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow.
simeyla commentedon Jul 4, 2020
TLDR; You probably can make use of the new
<const>which will narrow a string such as'Apple'to the typeApple. It's still a string, but it's now distinct from aBanana.See this question and my answer: https://stackoverflow.com/questions/37978528/typescript-type-string-is-not-assignable-to-type/55387357