import * as t from 'io-ts';
import { either, left, right } from 'fp-ts/Either';
import * as Either from 'fp-ts/Either';
import { PathReporter } from 'io-ts/lib/PathReporter';
import { identity } from 'fp-ts/function';
type StringWithUnderscore = `_${string}`;
const StringWithUnderscore = new t.Type<StringWithUnderscore, unknown, unknown>(
'StringWithUnderscore',
(u): u is StringWithUnderscore => t.string.is(u) && 0 < u.length && u.startsWith('_'),
(u, c) =>
either.chain(t.string.validate(u, c), s =>
0 < s.length && s.startsWith('_') ? t.success(s as StringWithUnderscore) : t.failure(u, c)),
identity,
);
describe('intersection with record whose keys are of type StringWithUnderscore', () => {
it('typechecks but does not pass validation', () => {
const Bar = t.intersection([
t.type({ foo: t.string }),
t.record(StringWithUnderscore, t.number)
]);
type Bar = t.TypeOf<typeof Bar>;
const bar: Bar = { foo: '', '_foo': 123 };
expect(() => decodeOrThrow(Bar, bar)).toThrow();
});
});
export function decodeOrThrow<A, O, I>(typ: t.Type<A, O, I>, i: I): A {
return Either.getOrElse<t.Errors, A>((err) => {
throw new Error(PathReporter.report(Either.left(err)).join('\n'));
})(typ.decode(i));
}
Bar.decode(bar) fails with
Invalid value "foo" supplied to : ({ foo: string } & { [K in StringWithUnderscore]: number })/1: { [K in StringWithUnderscore]: number }/foo: StringWithUnderscore
Expected behavior
Bar.decode(bar) should succeed
Reproducible example
See above
Suggested solution(s)
Additional context
I tried with a literal and that worked:
describe('intersection with record that has a literal as type', () => {
it('type-checks and passes validation', () => {
const Foo = t.intersection([
t.type({ foo: t.string }),
t.record(t.literal('bar'), t.number)
]);
type Foo = t.TypeOf<typeof Foo>;
const foo: Foo = { foo: '', bar: 123 };
expect(Foo.decode(foo)).toEqual(right(foo));
});
});
🐛 Bug report
Current Behavior
Bar.decode(bar)
fails withExpected behavior
Bar.decode(bar)
should succeedReproducible example
See above
Suggested solution(s)
Additional context
I tried with a literal and that worked:
Your environment