Inference uses only the values present, and where there are none it uses any
The common read: TypeScript works out a type from how a value is used, so leaving the annotation off costs nothing and the checker fills in whatever I meant.
Inference does not guess what you intended. A variable's type comes from its initializer, an array's element type comes from a best common type chosen from the candidates present, and where those candidates share a base class that no element of the array is, the handbook's own worked example says the compiler makes no inference about the element type and returns the union instead.
An initialized variable keeps the type its initializer gives it, and a later assignment of another type is an error. Two declarations work differently when noImplicitAny is on, which strict turns on: let x; with no initializer, and const a = []. Their type at each line is set by the most recent assignment or push above it. An unannotated parameter has no value for the compiler to read, so its type is any.
A flag, not a language rule, makes that fallback produce a diagnostic: noImplicitAny, which is on by default in 7.0.2 because strict defaults on. It reports an error where the compiler itself fell back to any (TS7006 on an unannotated parameter, TS7008 on a member of an object type with no type of its own), and it reports nothing about an any that came from a library signature or that you wrote yourself.
Because of that gap, a reviewer can write "this file is fully typed, strict is on, we are covered" on a configure(opts: { retries: number; onError: any }), which accepts any value in its second field and builds with no error, and three sprints later someone passes an object and reports a callback bug in the consumer. Annotate the boundaries and let inference type everything else. The annotation is the only way to state a type that the values themselves do not already imply.

