In a way, writing programs is just attaching names to pieces of data and then passing them trough functions.
We write elevation, but in reality, it will be 2864.
We write movie, but in reality, it will be {id = 3, title = "Parasite"}
We write request, but in reality, it will be GET /mondrian HTTP/1.1\n\r.
So the names that we throw around the code can represent very different data. To combat this, we usually have an idea of data shape behind the name. We express that idea by saying that:
elevationis of typeInt,movieis of type{id: Int, title: Text}, andrequestis of typeHttpRequest.
Regardless of language, each variable does have a type; at least a runtime one. It's just a matter of how strict we are about putting different date types behind a single name.
And in a strong-enough language, these type associations make a type system. It does require complete knowledge of types of all the variables, but ideally many of these types can be infered from their usage. In a perfect language, the program author would be forced to state just enough types of variables so that the program is well defined. That adds visibility and suggest what the author intended a variable to contain.
The type system can then see title + 1 and notice that it does not make sense, because artithmetic addition cannot be used on string. It is also the mechanism that can see request. and suggest .send().
It's a trade deal with the compiler, where you exchange key-strokes for a second pair of eyes on your code and suggestions on what keys to press next.
Another thing about types is that they are usually compile-time constructs; they are inferred and validated before the program is run.
This might be obvious, but think about it - the compiler is able to prove that some variable is always an integer, any integer and nothing else.
And this opens a door for many optimizations that would not be possible if we had to treat every variable as an arbitraty object. The compiler knows how much memory it needs for every variable. And for operations it can use static dispatch to apply exactly the correct operation without looking at the type at runtime.
For example, look at + operator in Rust. When applied to integers, it will perform arithmetic operations, but when applied to strings, it will do string concatenation. When we write a + b, Rust knows which operation exactly do we want, because it knows the types of a and b.
But in Python, this is not the case. There, compile-time static analysis does not exist. This means that when evaluation +, Python looks at the types at runtime and decides then which operation is needed.
This is a bit slow, but when run in the loop, it can become very very slow.
Essentially, knowing types at compile time allows moving computation from runtime into compile time.