           | {:max_restarts, non_neg_integer}
           | {:max_seconds, pos_integer}
 
+  @typedoc "Supported restart options"
+  @type restart :: :permanent | :transient | :temporary
+
+  # TODO: Update :shutdown to "timeout() | :brutal_kill" when we require Erlang/OTP 24.
+  #       Additionally apply https://github.com/elixir-lang/elixir/pull/11836
+  @typedoc "Supported shutdown options"
+  @type shutdown :: pos_integer() | :infinity | :brutal_kill
+
   @typedoc "Supported strategies"
   @type strategy :: :one_for_one | :one_for_all | :rest_for_one
 
+  @typedoc """
+  Supervisor type.
+
+  Whether the supervisor is a worker or a supervisor.
+  """
+  @type type :: :worker | :supervisor
+
   # Note we have inlined all types for readability
-  @typedoc "The supervisor specification"
+  @typedoc """
+  The supervisor child specification.
+
+  It defines how the supervisor should start, stop and restart each of its children.
+  """
   @type child_spec :: %{
           required(:id) => atom() | term(),
           required(:start) => {module(), function_name :: atom(), args :: [term()]},
-          optional(:restart) => :permanent | :transient | :temporary,
-          # TODO: Update :shutdown to "timeout() | :brutal_kill" when we require Erlang/OTP 24.
-          #       Additionally apply https://github.com/elixir-lang/elixir/pull/11836
-          optional(:shutdown) => pos_integer() | :infinity | :brutal_kill,
-          optional(:type) => :worker | :supervisor,
+          optional(:restart) => restart(),
+          optional(:shutdown) => shutdown(),
+          optional(:type) => type(),
           optional(:modules) => [module()] | :dynamic
         }
 

  which in our case would be invalid, which is why we always pass the initial
  counter explicitly.

  By replacing the map specification by `{Counter, 0}`, we keep the child
  specification encapsulated in the `Counter` module. We could now share our
  `Counter` implementation with other developers and they can add it directly
  to their supervision tree without worrying about the low-level details of
  the counter.

  Overall, the child specification can be one of the following:

    * a map representing the child specification itself - as outlined in the
      "Child specification" section

  section in the `GenServer` module docs.
  """

  # It is important to keep the two-arity spec because it is a catch-all
  # to start_link(children, options).
  @spec start_link(module, term) :: on_start
  @spec start_link(module, term, [option]) :: on_start
  def start_link(module, init_arg, options \\ []) when is_list(options) do
