Defining combos in ZMK is, similarly to QMK, a bit verbose.

{
    combos {
        compatible = "zmk,combos";
        
        combo_esc {
            timeout-ms = <50>;
            key-positions = <0 1>;
            bindings = <&kp ESC>;
        };

    };
};

QMK has a neat combo dictionary for this, instead of defining the same stuff over and over again in your keymap.c you just throw them all in an extra file and QMK will handle the rest.

We can do something similar in ZMK, instead of using a dictionary we create our own macro:

#define COMBO(NAME, BINDINGS, KEYPOS) \
  combo_##NAME { \
    timeout-ms = <50>; \
    bindings = <BINDINGS>; \
    key-positions = <KEYPOS>; \
    layers = <0>; \
  };

Once that's pasted at the top the .keymap file we can go ahead an use it like just like we'd use combos normally.

combos {
  compatible = "zmk,combos";
  
  COMBO(one, &kp N1, 0 10)
  COMBO(two, &kp N2, 1 11)
  COMBO(three, &kp N3, 2 12)
  COMBO(four, &kp N4, 3 13)
  COMBO(five, &kp N5, 4 14)
  COMBO(six, &kp N6, 5 15)
  COMBO(seven, &kp N7, 6 16)
  COMBO(eight, &kp N8, 7 17)
  COMBO(nine, &kp N9, 8 18)
  COMBO(zero, &kp N0, 9 19)
};

The first parameter is just an internal name, the second one the keypress we want to execute during the combo, and the last one is for the key positions.

All those combos would apply to layer 0 and would have a 50ms timeout, those are defined in the macro itself.
One could parameterize those as well, if necessary.

Here is a full example of a keymap using those combo-macros.