Comments (10)
@OliverJAsh I think you need to port the Json
Type from TypeScript to io-ts
. Something like (untested)
export const JsonArray = t.recursion('JsonArray', () => t.readonlyArray(Json))
export const JsonRecord = t.recursion('JsonRecord', () => t.record(t.string, Json))
export const Json = t.union([t.boolean, t.number, t.string, t.null, JsonArray, JsonRecord], 'Json')
Then you can use Json.is
from io-ts.
@gcanti Is this still possible without iots.prism
, which has been removed?
from io-ts.
Since I stumbled upon it myself and haven't seen it documented prominently io-ts
has pipe
since 1.0.0 which provides this functionality with something like:
const PersonFromString = JSONFromString.pipe(Person)
Quite handy
from io-ts.
validate
accepts value
s of type any
export type Validate<T> = (value: any, context: Context) => Validation<T>
so with io-ts
you can build types for any input: strings, numbers, objects, ...
Here's a gist
import * as t from 'io-ts'
import { tryCatch } from 'fp-ts/lib/Either'
export type JSONObject = { [key: string]: JSON }
export interface JSONArray extends Array<JSON> {}
export type JSON = null | string | number | boolean | JSONArray | JSONObject
// string -> JSON
const JSONFromString = t.prism(t.string, s => tryCatch<JSON>(() => JSON.parse(s)).toOption(), 'JSONFromString')
// JSON -> Person
const Person = t.interface({
name: t.string
}, 'Person')
function compose<A, B>(fa: t.Type<A>, fb: t.Type<B>, name: string): t.Type<B> {
return new t.Type<B>(
name,
(v, c) => fa.validate(v, c).chain(a => fb.validate(a, c))
)
}
// string -> Person
const PersonFromString = compose(JSONFromString, Person, 'PersonFromString')
console.log(t.validate(1, PersonFromString)) // Left([{"value":1,"context":[{"key":"","type":{"name":"PersonFromString"}}]}])
console.log(t.validate('{}', PersonFromString)) // Left([{"context":[{"key":"","type":{"name":"PersonFromString"}},{"key":"name","type":{"name":"string"}}]}])
console.log(t.validate('{"name":"Giulio"}', PersonFromString)) // Right({"name":"Giulio"})
from io-ts.
Interesting, thanks @gcanti.
I can't decide if I want to have distinct errors for parsing and validation, or whether I want to combine these, like you have done above.
With your first example above, the resulting error context doesn't tell you exactly where the validation failed. It's not clear that validation failed because of a parsing error:
Left([{"value":1,"context":[{"key":"","type":{"name":"PersonFromString"}}]}])
All that really tells me is "I couldn't fit 1 into the type PersonFromString". Using my custom reporter, a parsing error would appear as follows: Expected type to be PersonFromString, but got number: 1.
from io-ts.
For context, I'm working on twitter-api-ts, and I want parsing to be safe, i.e. return an Either
Left
instead of throwing an exception.
I'm already using io-ts to validate the API responses match what we expect.
When io-ts validation fails, we return a ValidationErrorsErrorResponse
. When parsing fails, we could return the same thing, or we could return a separate ParsingErrorErrorResponse
. I'm not sure what makes more sense here.
Related issue: OliverJAsh/twitter-api-ts#2
from io-ts.
Well, there are many options, you can customize the errors messages managing the context
function compose<A, B>(fa: t.Type<A>, fb: t.Type<B>, name: string): t.Type<B> {
return new t.Type<B>(
name,
(v, c) => fa.validate(v, c.concat({ key: 'parse', type: fa })).chain(a => fb.validate(a, c))
)
}
import { PathReporter } from 'io-ts/lib/reporters/default'
console.log(PathReporter.report(t.validate('bad', PersonFromString))) // [ 'Invalid value "bad" supplied to : PersonFromString/parse: JSONFromString' ]
console.log(PathReporter.report(t.validate('{}', PersonFromString))) // [ 'Invalid value undefined supplied to : PersonFromString/name: string' ]
or define your own little validation framework
type DecodeResult<A> = Either<string, A>
function json(value: any): DecodeResult<JSON> {
return t.validate(value, JSONFromString).mapLeft(() => 'Parsing error')
}
function type<A>(type: t.Type<A>): (value: JSON) => DecodeResult<A> {
return value => {
const validation = t.validate(value, type)
return validation.mapLeft(() => PathReporter.report(validation).join('\n'))
}
}
type Person = t.TypeOf<typeof Person>
function personFromString(value: any): DecodeResult<Person> {
return json(value).chain(type(Person))
}
console.log(personFromString('bad')) // Left("Parsing error")
console.log(personFromString('{}')) // Left("Invalid value undefined supplied to : Person/name: string")
io-ts
is kind of a low-level library you can build upon
from io-ts.
Looking at JSONFromString
, we lose the error object from JSON.parse
because we're converting from Either
to Option
:
const JSONFromString = t.prism(t.string, s => tryCatch<JSON>(() => JSON.parse(s)).toOption(), 'JSONFromString')
It would be good if io-ts allowed you to provide additional information (context?) for an error.
For example, JSON.parse
has errors such as:
- Unexpected token u in JSON at position 0
- Unexpected end of input
from io-ts.
For anyone who happens to land here: I made a little helper module that wraps JSON.parse
and io-ts validation: https://github.com/OliverJAsh/decode-ts
You have the choice of using the raw error objects returned or using the provided reporter.
from io-ts.
Here's a new version of JSONFromString
that uses the new Either.json
in fp-ts.
I'm not sure how to implement the is
function though 🤔
export const JSONFromString = new t.Type<E.Json, string, string>(
'JSONFromString',
// TODO:
(x): x is E.Json => true,
(s, c) =>
pipe(
s,
E.json((_e) => 'whatever'),
E.fold(() => t.failure(s, c), t.success),
),
JSON.stringify,
);
from io-ts.
Related Issues (20)
- Behavior changes and types are incorrect based on `intersection` array order
- Intersection with record whose keys are a custom type HOT 1
- io-ts recursion use issue HOT 4
- Subpath imports in ESM mode HOT 5
- Clarification question - how to work with the type of codecs themselves? HOT 1
- Surprising acceptance of various inputs HOT 1
- ReadonlyNonEmptyArray can't be used in a mapped type HOT 1
- How to generate documentation for types generated with `t.TypeOf` HOT 1
- Difficulties with generic serialisable type HOT 2
- [Question] Typing a generic mapped union HOT 1
- Inference error for `toString` property in intersection types with TypeScript 4.9.5 HOT 1
- `t.TypeOf<keyof<o>>` should return a string union type, not a numeric union type.
- PSA: TS 5.1 can break using this library HOT 1
- Intersection of Function with Object doesn't validate correctly
- t.Int: use Number.isSafeInteger instead of Number.isInteger
- non-enumerable records with extra keys do not pass `io-ts.record.is`, contrary to TypeScript types HOT 12
- partially enumerable record missing enumerable keys passes `record.is`, contrary to TypeScript types
- Records are stripped when encoding with t.record(NumberFromString, xxx) HOT 2
- Question: Convert a form to a type with different structure
- Incorrect behaviour when used with browser geolocation coordinates object
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from io-ts.