An integer range is a very basic type, too, conceptually, but many languages don't support them in the type system. You get an unsigned int type if you're lucky.
Not really, its semantics get hairy almost instantly. Eg does it incrementing it produce a new range?
Or do you give up on answering that and simply prevent adding strings and integers? When one wants to add them they can first manually apply an appropriate type conversion.
That is certainly a valid way to address your question – i.e. don't allow incrementing said type. Force converting it to a type that supports incrementing, and then from that the developer can, if they so choose, convert it back to an appropriate range type, including the original range type if suitable.
Of course, different languages will have different opinions about what is the "right" answer to these questions.
The original statement was about a range type, that is something like an integer that is statically constrained to a range of, say, 1..4 (1, 2, 3, 4).
To work with this as a type you need to have type level operations, such as adding two ranges (which can yield a disjoint range!), adding elements to the range, and so on, which produce new types. These all have to work on types, not on values. If 1..4 + 5..8 = 1..8 this has to happen at the type level, or, in other words, at compile-time.
Range types are very complicated types, compared to the types most people deal with.
Converting a string to an int is very simple to type (String => Int if you ignore errors) and adding integers is also simple to type ((Int, Int) => Int)
The quoted part above is an argument for dependent types. The conversion back to a range type creates a type that depends on a value, which is the essence of dependent typing.
But such a thing would be useful mostly for fields in data structures, and the runtime checks would add overhead. (Though, perhaps it would replace an array bounds check somewhere else?)
Just signed vs unsigned makes this a complex topic.