The {T} in ComplexNumber{T}(args...) is not part of the method signature, it’s part of the function name. At the language level, ComplexNumber(...) and ComplexNumber{T}(...) are two independent functions that just so happen to share a part of their names. This little demo might help appreciate this:
julia> struct Foo{T}
x::T
# Explicit inner constructor to eliminate the default outer constructor
Foo{T}(x) where {T} = new(x)
end
julia> methods(Foo{Int}) # This is the inner constructor we defined above
# 1 method for type constructor:
[1] Foo{T}(x) where T
@ REPL[2]:5
julia> methods(Foo) # Doesn't list the inner constructor because this is a different function
# 0 methods for type constructor
What is happening in your example is that this constructor
ComplexNumber(x::T, y::T) where {T<:Real} = ComplexNumber(promote(x, y)...)
dispatches to ComplexNumber, and the only method of this function is the ComplexNumber(x::T, y::T) where {T<:Real} which we started from; hence you get infinite recursion.
By contrast, this construct
ComplexNumber(x::T, y::T) where {T<:Real} = ComplexNumber{T}(promote(x, y)...)
dispatches to ComplexNumber{T} and therefore ends up calling the implicit inner constructor.