Dialyzer message help

Hi, can you please help me understand this one?

The call 'Elixir.Enrol.Classes':enroll_user
         (_enrollment_params@1 :: #{<<_:56, _:_*8>> => _}) breaks the contract 
          (#{'class_id' := integer(),
            'user_id' := integer(),
            any() => any()}) ->
             any()

I don’t understand this bit: (_enrollment_params@1 :: #{<<_:56, _:_*8>> => _})

What I am trying to say that this function expects a map with two required parameters class_id and user_id, both being integers.

The spec itself looks like this:

@spec enroll_user(%{
          :class_id => integer,
          :user_id => integer,
          optional(any) => any
        }) :: any

^ I put there return type to any for now to try to figure out my problem.

Thank you

The map that has only string keys does not match the expected type of a map that has at least the 2 required atom keys :class_id and :user_id.

Thank you. Is there a way to say the input struct must have keys “class_id” and “user_id”? I can’t seem to find or figure out the right syntax for that.

It’s already like that. foo: integer requires that the key :foo exists and it’s value is of type integer.

yes but I mean string key "class_id" rather than atom key :class_id. these are essentially params.

I do not remember if literal strings are allowed as key-types in a map, as they simply aren’t allowed everywhere, but if they are allowed required("foo") => any should do.

seems like one more tweak was needed: required(class_id :: String.t()) => pos_integer

Typespecs do not have the ability to specify specific string keys. Only atoms can be used as specific keys.

4 Likes

Which means, that there has at least one key of type string. It doesn’t say anything about the value of the string.

I see. I should not be getting string keys into this function in the first place, so I will refactor accordingly, and that will solve the problem, taking into account what @benwilson512 also wrote. Thank you both!