How to make a particular NTuple based Type concrete

I have a struct representing the remainders of an integer with respect to several moduli. All of the moduli fit into Int64 except for 2^64, so every modulus is either an Int64 or Type{Int64}.

struct LooseGoose{N} 
import Base: *
*(g::LooseGoose, r::Int64) = LooseGoose( mod.( widen.(g.remainders) .* r , g.moduli) .|> Int64 , g.moduli )
LooseGoose((4,7,12),(Int64,11,17)) * 7

This works fine, except in practice I always want the first modulus to be Int64 and the remaining moduli to be numeric. Is there a convenient way to specify this in the type definition without the Union. I can enforce this in a constructor.

struct StrictGoose{N} 

    function StrictGoose(remainders::NTuple{N,Int64}, moduli) where {N}
        moduli isa Tuple{DataType, (Int64 for i=2:N)...} || throw(ArguementError("moduli of incorrect form"))
StrictGoose((4,7,12),(Int64,11,17)) # Succeeds
StrictGoose((4,7,12),(13,Int64,17)) # Throws an error
  1. Is there a way to eliminate the Union and specify Tuple{DataType, (Int64 for i=2:N)...} more directly in the type definition?
  2. If the type of moduli is going to be restricted by the constructor anyway should I eliminate the annotation in the type declaration?
  3. Would I be better off using 0 as a sentinel value for Int64 so my Goose can be isbitstype, but then having to replace mod with something that watches for this value?
modif(a,b) = b==0 ? a : mod(a,b)

Do not be concerned that using
modnz(a,b) = iszero(b) ? a : mod(a,b) instead of mod(a,b)
would impact performance.
Do prefer working with an isbitstype(type) where performance matters and there is much computation is done.

You could do:

struct Goose{M} 

but note that M is one less than your N.

M being smaller than N is probably a feature here, since this formulation conveniently forces me to rethink whether I ever need 0-Tuples, which, of course, I don’t.