gcanti / io-ts

Runtime type system for IO decoding/encoding
https://gcanti.github.io/io-ts/
MIT License
6.71k stars 329 forks source link

Intersection of Function with Object doesn't validate correctly #701

Open niieani opened 1 year ago

niieani commented 1 year ago

🐛 Bug report

Current Behavior

decode() reports errors when trying to validate an intersection of a function with an object (i.e. a function with additional properties).

Expected behavior

No errors.

Reproducible example

https://codesandbox.io/s/io-ts-reproduction-2lcmt5

Expand full code ```ts import * as t from "io-ts"; export const featureConfigProperty = Symbol("config"); const FeatureActionFnValidator = t.Function; export type FeatureActionFn = t.TypeOf; const FeatureConfigValidator = t.type({ name: t.string }); const FeatureDefinitionValidator = t.intersection([ t.type({ actionFn: FeatureActionFnValidator }), FeatureConfigValidator ]); export type FeatureDefinition = t.TypeOf; const DefinedFeatureValidator = t.intersection([ FeatureActionFnValidator, t.type({ [featureConfigProperty]: FeatureConfigValidator }) ]); export type DefinedFeature = t.TypeOf; export const defineFeature = ({ actionFn, ...config }: FeatureDefinition): DefinedFeature => Object.assign(actionFn, { [featureConfigProperty]: config }); const definedFeature = defineFeature({ actionFn: () => {}, name: "a feature" }); // this goes Left, even though it should validate // it's a function with an additional property: console.log(DefinedFeatureValidator.decode(definedFeature)); ```

Suggested solution(s)

If an intersection contains mixed object with function, the decoder should only test for function type, and then validate the presence of properties.

My bet is that this code here is at fault (specifically, typeof u === 'object' fails):

https://github.com/gcanti/io-ts/blob/616583de0198632cad7820ed8701b15f654c7fd2/src/index.ts#L987-L1000 (also in https://github.com/gcanti/io-ts/blob/616583de0198632cad7820ed8701b15f654c7fd2/src/Guard.ts#L98C14-L100)

Additional context

Looks like a separate problem, but seems like Symbol properties aren't being validated correctly, ts-io treats them as if they didn't exist, though the TS types are correct. Might need to use Object.getOwnPropertySymbols in the code.

Your environment

Which versions of io-ts are affected by this issue? Did this work in previous versions of io-ts?

Software Version(s)
io-ts 2.2.20
fp-ts 2.13.1
TypeScript 5.1.3