Note: My answer assumes that Age and SampleType are not completely confounded. I have included the updated set-up code for such an example here:
SampleType <- rep(c("A", "B", "C"), 6)
Age <- rep(45:47, each=6) # Note use of "each"
Batch <- c(rep("X", 6),
rep("Y", 12))
Your desired design, ~0 + SampleType:Age + Batch
, is an exceptionally odd one, since it includes an interaction term between SampleType and Age without including the main effects of either SampleType or Age, nor including an intercept. This means that there is nothing in the design capable of absorbing the first level of Batch, so you get a design with separate coefficients for both batches. This is also a design that is unlikely to represent any kind of reality. If I'm not mistaken, this design effectively assumes that all three SampleTypes have the same expression at Age 0, which is unlikely to be a useful assumption if your operative age range is in the mid-40s. Much more likely is that you would want to include main effect coefficients for SampleType in addition to the interaction, using a formula of ~0 + SampleType + SampleType:Age + Batch
. This would fit a separate Age slope and intercept to each level of SampleType, which is a very reasonable design. In such a design, the BatchX coefficient is properly absorbed, as you expect:
> colnames(model.matrix(~0 + SampleType:Age + Batch))
[1] "BatchX" "BatchY" "SampleTypeA:Age" "SampleTypeB:Age"
[5] "SampleTypeC:Age"
> colnames(model.matrix(~0 + SampleType + SampleType:Age + Batch))
[1] "SampleTypeA" "SampleTypeB" "SampleTypeC" "BatchY"
[5] "SampleTypeA:Age" "SampleTypeB:Age" "SampleTypeC:Age"
It would be nice to know what the different factors are for each sample. Stuff like this sometimes corresponds to linear dependencies between your factors, but it's hard to say for sure.
I've added a simple example to illustrate my point. The first, second and forth colnames calls all absorb the first batch term, whereas the third doesn't. I'm basically trying to figure out why the third doesn't absorb the batch variable.