'How can I determine the return type of a method of a generic type in python?

I have a set of classes that all implement a specific method and this method returns a different type depending on which class implements it. I want to pass in one of those classes as a generic type var into another class. Within the second class, is it possible using python to determine the return type of the method defined in the other classes?

# Set of classes that implement the specific function, here being `shared_function`
class SuperClass:
    def shared_function(self) -> Any:
        # The use of Any here is a placeholder. 
        # I've tried having `SuperClass` subclass some `Generic` 
        # but even if I do, I can't quite figure out how 
        # the class U below could somehow figure out what the generic variable is
        ...

class Sub1(SuperClass):
    def shared_function(self) -> int:
        ...

class Sub2(SuperClass):
    def shared_function(self) -> str:
        ...

# The class I want to pass it into
@dataclass
class U(Generic[_T]):
    t: Type[_T]
    
    def get_utility_type() -> ?:
        # I want the return type of this function 
        # to be the same as the return type of `shared_function` for the relevant subclass
        # for example, if `_T` was `Sub1`, the return type here should be `int`
        # if `_T` was `Sub2` the return type should be `str`
        ...

The behavior I would want is

foo = U(t=Sub1)
returned_foo = foo.get_utility_type()
reveal_type(returned_foo) # Type of "returned_foo" is "str"
# `returned_foo ` should be of type str. Note `returned_foo` shouldn't be the type str but rather an instance of str
# similarly

boo = U(t=Sub2)
returned_boo = foo.get_utility_type()
reveal_type(returned_boo) # Type of "returned_boo" is "int"

We can assume that I'll never pass SuperClass into U's constructor and modifications to SuperClass, Sub1, and Sub2 can be made to try to support this kind of functionality.

Any help would be greatly appreciated.

As a point of reference, in typescript this is fairly trivial to implement

class SuperClass<T> {
  constructor() {
    //
  }

  shared_function = (): T | null => {
    return null;
  };
}

class Sub1 extends SuperClass<number> {
  shared_function = () => {
    return 0;
  };
}

class Sub2 extends SuperClass<string> {
  shared_function = () => {
    return 'abc';
  };
}

class U<T extends InstanceType<typeof SuperClass>> {
  get_utility_type = (): ReturnType<T['shared_function']> | null => {
    return null;
  };
}

const u1 = new U<Sub1>();
const inferredAsNumber = u1.get_utility_type();
const u2 = new U<Sub2>();
const inferredAsString = u2.get_utility_type();


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source