Using the same paradigm as in RDF, I'm modelling a class and its non-literal attributes as the id to the referenced object, like a URI in RDF. For example: GUID = NewType("GUID", int) # create a subclass of int print(GUID.__name__) # prints 'GUID' class Person: id: GUID parents: list["PersonId"] When it comes to define PersonId, I have this piece of code: def field_info(class_model: type, attr_name: str) -> tuple[type, str, Any]: attributes = inspect.get_annotations(class_model) assert attr_name in attributes return (class_model, attr_name, attributes[attr_name]) PersonId = TypeAliasType("PersonId", Annotated[GUID, field_info(Person, "id")]) # create an alias of GUID. and in the code, near the serialisation/deserialisation in the DB, I have checks like those to verify that the Annotation's type corresponds to the attribute's type. (I took an example to simplify. The real code is agnostic from the Person class.) assert Person.__name__ == "Person" # true assert isinstance(PersonId, typing.TypeAliasType) # true origin = get_origin(PersonId.__value__) args = get_args(PersonId.__value__) assert origin is Annotated # true assert args[0] is GUID # true assert args[1] == (Person, "id", GUID) # true assert args[0] == args[1][2] # true Is there be a way to directly define PersonId = TypeAliasType("PersonId", Person.id) #AttributeError: type object 'Person' has no attribute 'id' Continue reading...