Optional booleans?
Alex Lohr

Alex Lohr @lexlohr

About: ...former musician, voice actor, martial artist, started coding 38 years ago and turned front-end developer 25+ years ago.

Location:
Germany
Joined:
Jun 13, 2017

Optional booleans?

Publish Date: Jan 16 '20
8 10

If you define APIs, you usually discuss names and types a lot. One of these discussions brought up that one of my colleagues' dislike of optional boolean properties - and there's also a good reason for it, because undefined and false are coerced to the same value, which can lead to confusion.

I personally value the developer experience of having sensible defaults whenever possible over the possible misunderstanding of false and undefined.

Any other advantages and disadvantages I have overlooked? Please discuss!

Comments 10 total

  • Ben Sinclair
    Ben SinclairJan 16, 2020

    If something can have three states, it shouldn't be a boolean. If it needs to have one of two states, then existing data should be updated with the default (whether that's true or false).

    "Optional booleans" only really pop up when boolean has been added to a schema, but the developers haven't made it required because they don't want to break backwards-compatibility. They should set the field to the default on all existing records.

    • Alex Lohr
      Alex LohrJan 16, 2020

      Sorry, but then you just get a lot of enums

      enum Whatever {
        Default,
        On,
        Off
      }
      

      I don't think that's an improvement, rather the contrary.

      Especially for a web/react/Vue/etc component, that makes no sense at all.

      • Daniel Brady
        Daniel BradyJan 16, 2020

        Hmm, I interpreted Ben's comment about 'defaults' as "always have one, and when adding one retroactively be sure to backfill existing data," not "have a 'default' value that is distinct from all others in the type". This seems to align with and extend your own comment:

        I personally value the developer experience of having sensible defaults whenever possible

        Did I read you right, Ben? (@moopet )

        • Alex Lohr
          Alex LohrJan 16, 2020

          But if you say booleans cannot be optional, then the consequence is that there cannot be default values for boolean properties.

          • Daniel Brady
            Daniel BradyJan 16, 2020

            I think that makes sense, though, right? If it's required, a default isn't necessary or applicable. If it's optional, you need a sane default.

            But I didn't say anything about not having optional booleans: quite the contrary, I was talking about the case where you do have optional booleans, and paraphrasing Ben's comment about it.

            • Ben Sinclair
              Ben SinclairJan 16, 2020

              @dabrady yeah, that's what I meant. If there's a useful semantic difference between three states then it should be an enum or something, but if the third state is just there beacuse it's unfilled, why not fill it?

              • Alex Lohr
                Alex LohrJan 18, 2020

                Because it might only be for one of the use cases of your API. Should the developer using it need to care about all the use cases that don't matter to him? I should think that is a waste of his time.

  • Pacharapol Withayasakpunt
    Pacharapol WithayasakpuntJan 16, 2020

    If you use TypeScript probably also with runtypes, it's easy to do 'x' | 'y' | 'z', and all are truthy.

    I believe API server should validate the input anyway (actually client-side as well), probably with JSON Schema and AJV.

    But it might can be done with runtypes as well.

  • Daniel Brady
    Daniel BradyJan 16, 2020

    Optional booleans in APIs make sense to me in the same way that optionals in general make sense: they aren't optional to the API implementation, they're optional to the API consumer.

    It's important to remember an optional boolean is not a boolean, it is a union type of boolean and whatever the type is for "value absence".

    For many languages, this is tricky to work with, but Typescript and JavaScript make it easy.

    A lot of people will rely on the simplicity of type coercion for implementing presence checks: if the value is "falsey", it's considered "absent."

    function foo(x) {
      if (x) {
        alert("x is present");
      } else {
        alert("x is absent");
      }
    }
    

    But this of course breaks when the value is false exactly!

    Instead, we should remember that in JS variables don't have types, only values do, and so doing a "presence check" on something that is supposed to be a boolean is as simple as asking typeof x == "boolean"!

    function foo(x) {
      if (typeof x == "boolean") {
        alert("x is present");
      } else {
        alert("x is absent or not a boolean");
      }
    }
    

    In JS, if no value was provided, the type will be the string "undefined", and if a value is provided but isn't true or false the type will be something else.

    This is amazing because it avoids the bugs that can happen when you try to determine presence based on the value itself.

    • Alex Lohr
      Alex LohrJan 16, 2020

      Consider this more modern TS example:

      const whatever = ({ darkMode = false, ...rest }) => null
      

      It is not three different values, but a boolean value with a default so it can be optional.

      If this was a component, every user would be forced to declare the darkMode property like it or not if it was not optional, which to me makes no sense if you can have a default.

Add comment