It is difficult—probably impossible—to write this as a type conforming to ViewModifier. You give this type for then:
var then: (_: Content) -> Content
However, you're usually going to want then to return a different type than it was given, because that's how modifiers generally work. For example, Text("hello") has type Text, but Text("hello").background(Color.red) has type ModifiedContent<Text, _BackgroundStyleModifier<Color>>.
We can try making the modifier generic over the type returned by then:
struct If<Then: View>: ViewModifier {
    var test: Bool
    @ViewBuilder
    var then: (_: Content) -> Then
    @ViewBuilder
    func body(content: Content) -> some View {
        if test {
            then(content)
        } else {
            content
        }
    }
}
But then we run into a problem when we try to use the modifier:
struct ExampleView: View {
    var isActive: Bool
    var body: some View {
        HStack {
        }.modifier(If(test: isActive, then: { $0.background(.red) }))
                // ^ 🛑 Generic parameter 'Then' could not be inferred
    }
}
Why can't it infer the type? The type would be ModifiedContent<Content, _BackgroundStyleModifier<Color>>. But Content is actually a type alias for _ViewModifier_Content<If>… except that we made If generic over Then, so Content is a type alias for _ViewModifier_Content<If<ModifiedContent<Content, _BackgroundStyleModifier<Color>>>>. Oops, we just put Content back in there. The Content type alias and the Then type are mutually recursive, and Swift cannot handle an infinitely-nested generic type.
It is, however, possible to achieve a similar effect by extending View with a modifier method. You can find lots of web pages with examples by searching for “swiftui conditional modifier”. It typically looks like this:
extension View {
    @ViewBuilder
    func `if`<Content: View>(
        _ condition: Bool,
        @ViewBuilder transform: (Self) -> Content
    ) -> some View {
        if (condition) {
            transform(self)
        } else {
            self
        }
    }
}
and you use it like this:
struct ExampleView: View {
    var isActive: Bool
    var body: some View {
        HStack {
        }.if(isActive) { $0.background(.red) }
    }
}
Note, however, that a modifier like this can cause animations and transitions to misbehave. The problem is that SwiftUI generally considers transform(self) and self to have different identities because they're in separate branches of the if statement. This affects how SwiftUI animates them. This may or may not be a problem for you, but it is why the general advice from Apple is to use “inert” forms of modifiers directly, instead of introducing an if-style modifier. That is, Apple would recommend this instead, for the example above:
struct ExampleView: View {
    var isActive: Bool
    var body: some View {
        HStack {
        }.background(isActive ? .red : .clear)
    }
}
Here, there's no if statement, so SwiftUI will recognize the HStack as being the same across changes to isActive, for animation purposes.