Static types are fixed. Variables are declared as specific types and their types don't change.
// x is forever an int int x;
Dynamic types need not be declared and the type can change. Variables can be initialized with one type and later assigned a different one.
// x is an integer type of some kind var x = 10; // Now it's a string type x = 'ten'
Type inference occurs when no type is specified and the compiler determines what the type should be based on the type of the initializer. The dynamic type example I show above uses type inference for both the initialization and the assignment. C++ and D also support it.
// In this D code, x is inferred to be an int and y a float, // but they are still statically typed (i.e. the types can't be changed) auto x = 10; const y = 20.0f;