A type system's main purpose is to reduce software bugs. Types are assigned to each data value. Rules define what operations each data type supports. Checking determines whether our code follows those rules. Type safety means that the code follows all the type rules.
Kinds of Types
Static vs Dynamic
Static types are known and checked at compile-time.
Dynamic types are known and checked at run-time.
In other words, if the compiler knows the type, it's static. If the compiler cannot know the type, it's dynamic. The actual run-time value of a dynamic type determines the operations it supports.
Strong vs Weak
strong types provide less freedom of type conversion
weak types provide more freedom of type conversion
Explicit vs Implicit
explicit our code tells the compiler the data type
implicit the compiler figures out the data type
Safe vs Unsafe
safe we must treat the raw-bytes as a compatible type
unsafe we are able to treat the raw-bytes as whatever type we want
Compile-time vs Run-time
compile-time the time at which we build the code
run-time the time at which we execute the code (aka execution-time)
covariance a more specific type of data ("cat") can go into a more general type of data ("animal") Animal a = new Cat();
contravariance a more general type of data ("animal") can go into a more specific type of data ("cat") CatComparer cc = new AnimalComparer(); cast, changing the compile-time type to fit the actual run-time type convert, changing data from one type into another