io-ts-types
io-ts-types copied to clipboard
Add case insensitivity for string type literals
it's helpful to define an in put as being t.keyof({ firstName: null, preferredName: null });
but it would be nice if they could send firstName
, firstname
, fIrStNaMe`
@piersmacdonald we strive keeping io-ts mininal and lean. Io-ts-types repo is where such additional Types may be defined.
Fair enough, thanks for the quick response.
@piersmacdonald Did you come up with this type? I couldn't find it in io-ts-types.
@gcanti is there easy way to achieve this with current Decoder?
One way to do this is to have all literals in lowercase and then convert incoming string to lowercase and then forward it to D.literals decoder
But this forces me to define all literal types in lowercase.
const lowerCase = pipe(
D.string,
D.map((s) => s.toLowerCase())
)
const ComponentType = pipe(lowerCase, D.compose(D.literal('hcd', 'assembly')))
type ComponentType = D.TypeOf<typeof ComponentType>
console.log(ComponentType.decode('HCD'))
@kpritam I would return the original input
import * as E from 'fp-ts/lib/Either'
import { flow } from 'fp-ts/lib/function'
import * as D from 'io-ts/lib/Decoder'
const decoder: D.Decoder<unknown, string> = {
decode: flow(
D.string.decode,
E.chainFirst((s) => D.literal('hcd', 'assembly').decode(s.toLowerCase()))
)
}
console.log(decoder.decode('HCD'))
// => { _tag: 'Right', right: 'HCD' }
@gcanti but this forces to define all the literals in lowercase, that might not what you always want.
Basically is it possible to make decoding of following literals case insensitive?
const compType = D.literal('Hcd', 'Assembly')
// all the following cases should pass and all should return **Hcd**
compType.decode('hcd')
compType.decode('Hcd')
compType.decode('HCD')
all the following cases should pass and all should return Hcd
Ah ok, I misinterpreted the requirements, I thought you always wanted the original input back
import * as E from 'fp-ts/lib/Either'
import { flow, pipe } from 'fp-ts/lib/function'
import * as O from 'fp-ts/lib/Option'
import * as A from 'fp-ts/lib/ReadonlyArray'
import * as D from 'io-ts/lib/Decoder'
export const iliteral = <A extends readonly [string, ...Array<string>]>(
...values: A
): D.Decoder<unknown, A[number]> => {
const message = values.map((value) => JSON.stringify(value)).join(' | ')
return {
decode: flow(
D.string.decode,
E.chain((s) =>
pipe(
values,
A.findIndex((value) => value.toLowerCase() === s.toLowerCase()),
O.fold(
() => D.failure(s, message),
(i) => D.success(values[i])
)
)
)
)
}
}
@gcanti great, thanks.
Does it make sense for this to go into main library or io-ts-types?
It is pretty common use case that these literal types are defined as case insensitive enums in other languages and then its upto codec library to decide how to encode/decode them.
@kpritam ended up switching libraries for the job. This question was asked in the context of validating user requests for a REST service. So I switched to class-validator
which is more specific to my use case. Still make some use of io-ts
but not in that context.
Does it make sense for this to go into main library or io-ts-types?
:+1: once Decoder becomes official, moving to io-ts-types
as a reminder