From ispiro's suggestion I found something that should cover everything.
So I declare my interfaces independent of underlying representation, e.g.
public interface IPostNumber{}
public interface IPostNumberFrom : IPostNumber{}
public interface IPostNumberTo : IPostNumber{}
These have full interface generality such as multiple inheritance. Then the data representation is done with generic classes with implicit conversion:
public class CInt<T>
{
public int value;
public static implicit operator int(CInt<T> d) => d.value;
public static implicit operator CInt<T>(int b) => new CInt<T>() { value = b };
}
Functions that accepts IPostNumber with int, is done as such:
private int TestPostNumberInt(CInt<IPostNumber> i) => i;
private int TestPostNumberFrom(CInt<IPostNumberFrom> i) => i;
CInt<IPostNumber> a = 4; // Works
Assert.Equal(1, TestPostNumberInt(1)); // Works
Assert.Equal(1, TestPostNumberFrom(a)); // Don't compile with IPostNumber into IPostNumberFrom
Now I can always declare CString<IPostNumber>, if some post numbers are represented with string. Or a function could accept the IPostNumber interface itself, if I make some class of it. Now one little issue is that if I want to pass CInt<IPostNumberFrom> to TestPostNumber, the method must be generic with T : IPostNumber, like this:
private int TestPostNumberInt<T>(CInt<T> i) where T : IPostNumber => i;
private int TestPostNumberIntFrom<T>(CInt<T> i) where T : IPostNumberFrom => i;
and then the generic type will not be detected while using implicit conversion (must cast). But we'll see if this is a big deal.
Also for later consideration: I will have class CJSON<T> : CString<T>. From what I see it works, though argubly CJSON could have different representations as well, like byte[] in some context. (But this is taking things far). So just need to think hard about representation vs. interfaces for my domain concepts.