How to wrap MetaGraphsNext.MetaDiGraph effectively?

So I need a DAG for a project but I’m really struggling to wrap MetaDiGraph in a wrapper struct to ensure that it is in fact a DAG. I eventually gave up and just left it untyped but this has been really bothering ever since, since I’m sure that it must be possible somehow.

I managed to correctly (I assume) define the struct itself

struct DAG{V, D, _F}
    inner::MetaGraphsNext.MetaDiGraph{Int, V, Graphs.SimpleDiGraph{Int}, D, Nothing, Nothing, _F, Float64}

    function DAG(graph::MetaGraphsNext.MetaDiGraph{Int, V, Graphs.SimpleDiGraph{Int}, D, Nothing, Nothing, _F, Float64}) where {V, D, _F}
        @assert Graphs.is_directed(graph)
        @assert !Graphs.is_cyclic(graph)
        new{V, D, _F}(graph)
    end
end

which works fine when used like this

julia> DAG(MetaGraphsNext.MetaGraph(Graphs.DiGraph(), Label = String, VertexData = Symbol))
DAG{String, Symbol, MetaGraphsNext.var"#3#5"}(Meta graph based on a {0, 0} directed simple Int64 graph with vertex labels of type String, vertex metadata of type Symbol, edge metadata of type Nothing, graph metadata given by nothing, and default weight 1.0)

but from there I’m running into two problems.

The first one is that I want to define a zero-argument constructor that I can use to avoid having to specify the full MetaGraph constructor each time. I tried this in a few different ways but the two that felt closest to being correct were

DAG(::Type{V}, ::Type{D}) where {V, D} = let f = x -> 1.0
    DAG{V, D, typeof(f)}(MetaGraphsNext.MetaGraph(Graphs.DiGraph(), Label = V, VertexData = D, weight_function = f))
end

and:

DAG(V}, ::Type{D}) where {V, D} = let f = x -> 1.0
    DAG{V, D, typeof(f)}(MetaGraphsNext.MetaGraph(Graphs.DiGraph(), Label = V, VertexData = D, weight_function = f))
end

Unfortunately neither of these work…

julia> DAG(String, Symbol)
ERROR: MethodError: no method matching DAG{String, Symbol, var"#1#2"}(::MetaGraphsNext.MetaDiGraph{Int64, String, Graphs.SimpleGraphs.SimpleDiGraph{Int64}, Symbol, Nothing, Nothing, var"#1#2", Float64})
Stacktrace:
 [1] DAG(#unused#::Type{String}, #unused#::Type{Symbol})
   @ Main ~/a/path/to/project/dag.jl:33
 [2] top-level scope
   @ REPL[1]:1

julia> DAG{String, Symbol}()
ERROR: MethodError: no method matching DAG{String, Symbol, var"#1#2"}(::MetaGraphsNext.MetaDiGraph{Int64, String, Graphs.SimpleGraphs.SimpleDiGraph{Int64}, Symbol, Nothing, Nothing, var"#1#2", Float64})
Stacktrace:
 [1] (DAG{String, Symbol})()
   @ Main ~/a/path/to/project/dag.jl:33
 [2] top-level scope
   @ REPL[1]:1

The other problem that I’m facing is that I can’t create a type alias for DAG to avoid having to specify the types each time. Normally I’d do something like this

const SpecificDAG = DAG{String, Symbol}

but if I do that then I can’t build the type anymore

julia> StringSymbolDAG(MetaGraphsNext.MetaGraph(Graphs.DiGraph(), Label = String, VertexData = Symbol))
ERROR: MethodError: no method matching (StringSymbolDAG)(::MetaGraphsNext.MetaDiGraph{Int64, String, Graphs.SimpleGraphs.SimpleDiGraph{Int64}, Symbol, Nothing, Nothing, MetaGraphsNext.var"#3#5", Float64})
Stacktrace:
 [1] top-level scope
   @ REPL[2]:1

which is not what happens if I do a similar thing for Dict (as an example)

julia> const SpecificDict = Dict{String, Symbol}
Dict{String, Symbol}

julia> SpecificDict()
Dict{String, Symbol}()

julia> SpecificDict("a" => :a)
Dict{String, Symbol} with 1 entry:
  "a" => :a

I feel like there’s just something about parametric types that I just don’t understand, but one step at a time I guess. Can anyone help me out here?