How to set default options in types.submodule

Hello, I want to define a module that has nested options with a default. I’ve tried something like this:

{ config, lib, ... } : 
with lib;
let
  cfg = config.services.foo;
in
{
  options.services.foo = {
    strA = mkOption {
      type = types.str;
      default = "A";
    };
    sub = mkOption {
      default = { };
      type = types.attrsOf ( types.submodule {
        options = {
          strB = mkOption {
            type = types.str;
            default = "B";
          };
        };
      });
    };
  }
  config = {
    ...
    # error within config when trying to use cfg.sub.strB
  };
}

I’ve made something similar and try to use the module something like this without specifying the submodule:

services.foo = {
  strA = "notA";
}

It complains when implementing the config with cfg.sub.strB that error: attribute 'strB' missing. I thought that this would resolve to the default of strB but it does not. Still trying to figure out attrsOf and types.submodule so I’m not sure what i’m missing?

You have the default value of options.services.foo.sub set to {}, so naturally there is no strB attribute inside an empty attrset.
Secondly, you’re using attrsOf submodule, not just submodule, so the correct path would be cfg.sub.whatever.subB (replace whatever of course).

If you were to set config.services.foo.sub.whatever = { };, then cfg.sub.whatever.subB would have a value of "B", that’s where that default comes in.

1 Like

Yeah I was misinterpretting attrsOf, I just got rid of attrsOf and it works how I was expecting now. This is my “final” options for the module

options.services.foo = {
    strA = mkOption {
      type = types.str;
      default = "A";
    };
    sub = mkOption {
      default = { };
      type = types.submodule {
        options = {
          strB = mkOption {
            type = types.str;
            default = "B";
          };
        };
      };
    };
  }