The other answers are very nice, but I want to emphasize one point. Our programming language theory of terms is based strongly on the Lambda Calculus. A "pure" Lisp corresponds (more or less) to a heavily-sugared untyped Lambda Calculus. The meaning of programs is defined by the evaluation rules that say how the Lambda Calculus terms are reduced as the program runs.
In a typed language, we assign types to terms. For every evaluation rule, we have a corresponding type rule that shows how the types are preserved by evaluation. Depending on the type system, there are also other rules defining how types relate to one another. It turns out that once you get a sufficiently interesting type system, the types and their system of rules also correspond to a variant of the Lambda Calculus!
Although it's common to think of Lambda Calculus as a programming language now, it was originally designed as a system of logic. This is why it is useful for reasoning about the types of terms in a programming language. But the programming language aspect of Lambda Calculus allows one to write programs that are evaluated by the type checker.
Hopefully you can see now that "type-level programming" is not a substantially different thing than "term-level programming", it's just that it's not very common now to have a language in a type system that's powerful enough that you'd have a reason to write programs in it.