You’re still defining your resources declaratively - just with type checking - at least if you’re using a statically typed language. If not, you’re still just getting runtime errors when you run your program. It might catch some things before you run your template - but so will a linter.
Neither will catch issues like using a subnet that doesn’t exist or using an AMI that doesn’t exist.