Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
niieani opened this issue Aug 17, 2023 · 0 comments
Open

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

niieani opened this issue Aug 17, 2023 · 0 comments

Comments

@niieani
Copy link

niieani commented Aug 17, 2023

馃悰 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
import * as t from "io-ts";

export const featureConfigProperty = Symbol("config");

const FeatureActionFnValidator = t.Function;
export type FeatureActionFn = t.TypeOf<typeof FeatureActionFnValidator>;

const FeatureConfigValidator = t.type({
  name: t.string
});

const FeatureDefinitionValidator = t.intersection([
  t.type({
    actionFn: FeatureActionFnValidator
  }),
  FeatureConfigValidator
]);
export type FeatureDefinition = t.TypeOf<typeof FeatureDefinitionValidator>;

const DefinedFeatureValidator = t.intersection([
  FeatureActionFnValidator,
  t.type({
    [featureConfigProperty]: FeatureConfigValidator
  })
]);
export type DefinedFeature = t.TypeOf<typeof DefinedFeatureValidator>;

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):

io-ts/src/index.ts

Lines 987 to 1000 in 616583d

export class AnyDictionaryType extends Type<{ [key: string]: unknown }> {
/**
* @since 1.0.0
*/
readonly _tag: 'AnyDictionaryType' = 'AnyDictionaryType'
constructor() {
super(
'UnknownRecord',
(u): u is { [key: string]: unknown } => u !== null && typeof u === 'object' && !Array.isArray(u),
(u, c) => (this.is(u) ? success(u) : failure(u, c)),
identity
)
}
}

(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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant