mkOptionType explanation

I am a bit confused about mkOptionType. Ok, I get that it is used to define custom types for the NixOS DSL. I’d like to understand how that works in the case of simple-nixos-mailserver / nixos-mailserver · GitLab.

I can see:

  loginAccount = mkOptionType {
          name = "Login Account";

or to give you some context

loginAccounts = mkOption {
      type = types.attrsOf (types.submodule ({ name, ... }: {
        options = {
          name = mkOption {
            type = types.str;
            example = "";
            description = "Username";

   extraVirtualAliases = mkOption {
      type = let
        loginAccount = mkOptionType {
          name = "Login Account";
          check = (account: builtins.elem account (builtins.attrNames cfg.loginAccounts));
      in with types; attrsOf (either loginAccount (nonEmptyListOf loginAccount));
      example = {
        "" = "";
        "" = "";
        "" = "";
        "" = [ "" "" ];
      description = ''
        Virtual Aliases. A virtual alias `"" = ""` means that
        all mail to `` is forwarded to ``. Note
        that it is expected that `` and `` is
        forwarded to some valid email address. (Alternatively you can create login
        accounts for `postmaster` and (or) `abuse`). Furthermore, it also allows
        the user `` to send emails as ``.
        It's also possible to create an alias for multiple accounts. In this
        example all mails for `` will be forwarded to both
        `` and ``.
      default = {};

where is this type Login Account used at all? I mean there is some loginAccounts but how does that fit together?

Specifically loginAccount type is defined with let expression inside of extraVirtualAliases. So how can a member of attrset loginAccounts be of type loginAccount or as a matter of fact how is a value of type loginAccount even constructed? I was grepping through the code and can’t find any other mention of loginAccount*.

Here we have the check attribute, which holds a predicate.

Each value that you can pass into this predicate and that will then return true is a valid value of the type loginAccount.

Thanks for explanation. Thought this check was just for validation purposes.

You could do check = lib.const true and then everything that exists is of type loginAccount? I thought you’d need some type constructor to construct an instance (ala LoginAccount "some@email"). But yeah, I’m probably overcomplicating since I was reading a book about Haskell.