The annotations for an object (which can be a class or a function as well) are stored in its __annotations__ property, as defined by PEP 526 and introduced in Python 3.6. This gives rise to the following solution:
The following approach does workworks as I suspect you're looking for:
>>> clss = type('TestPropGrp2', (bpy.types.PropertyGroup,), { ... '__annotations__': {'myProp': (StringProperty, {'default': 'MyStr'})} ... }) >>> print(clss.__annotations__) {'myProp': (<built-in function StringProperty>, {'default': 'MyStr'})} This also allows you to see that your proposed "one way to do it" approach doesn't actually work as intended:
>>> clss = type('TestPropGrp', (bpy.types.PropertyGroup,), {}) >>> clss.myProp : StringProperty(default='MyStr') >>> print(clss.__annotations__) Traceback (most recent call last): File "<blender_console>", line 1, in <module> AttributeError: type object 'TestPropGrp' has no attribute '__annotations__'