I.e. "The two or three popular dynamic programing languages I know don't have any compile-time checks".
Even my modest dialect (utterly not focused on type at all) Lisp can do a thing or two:
1> (compile-toplevel '(let (x) (cons a)))
** expr-1:1: warning: unbound variable a
** expr-1:1: warning: cons: too few arguments: needs 2, given 1
** expr-1:1: warning: let: variable x unused
#<sys:vm-desc: 8da2ac0>
2> (compile-toplevel '(awk ((let (x y) (rng x y)) (prn))))
** expr-2:1: warning: rng: form x
is moved out of the apparent scope
and thus cannot refer to variables (x)
** expr-2:1: warning: rng: form y
is moved out of the apparent scope
and thus cannot refer to variables (y)
** expr-2:1: warning: unbound variable x
** expr-2:1: warning: unbound variable y
** expr-2:1: warning: let: variable y unused
** expr-2:1: warning: let: variable x unused
#<sys:vm-desc: 8e6f3c0>
3> (compile-toplevel 'foo.bar)
** expr-3:1: warning: qref: bar isn't the name of a struct slot
** expr-3:1: warning: unbound variable foo
#<sys:vm-desc: 8e8dc80>
Common Lisp implementations like SBCL have sophisticated type inference.Dynamic programs can do things wrong in ways that are statically obvious.