This is a good post and technique in general.
I used to use it extensively in C++: beware though of (super long) compile errors while nesting them through templates params. :)
Edit: I was wrapping pointers for instance to not mix GPU and CPU memory addresses, to define a unidirectional flow of memory transfers between devices (and avoid manual synchronisation and tracking)... etc.
You could somehow define a state machine through types and ensure compositional coherence.