ParseTreeProperty is a convenience for "attaching" properties to nodes of your parse tree, and could be a useful way to keep track of the type of each node in your tree. However, as the comments mention, there are other data structures you can you to track the type of each node and map back to it. (Note: if you use this approach with a listener, as your question implies, you'd need to implement it in the *Exit() method, as you would want all the children to have been "listened to" and their types assigned, so that you can determine the type of the parent expression.)
Using a listener, you can also just have a stack of types. When you exit each expression, it pops the types of all of its children, evaluates the expression type for itself, and pushes that type on the stack. You, of course, have to take care to properly manage to pushing and popping (look out for exceptions), but it can be a reasonably clean implementation.
You could also implement an expression type validation visitor. With this approach, you write an expression visitor that returns it's type. With each overriden visit*() you can just call visit() on each child to get it's type, and then decide what you want to resulting type to be (and probably whether it's even a valid expression). Notice that ```visit``ing a node return a result with visitors, this is one of the key differences between visitors and listeners (the other being, that, with visitors, you ave to explicitly choose how to navigate your child nodes).
So far as "what to do with this data", at this point you're making design decisions about how you want your language to behave, what's valid, etc.
For example:
7 * "string"
Maybe you decide 7 is an Int type and "string" is a String type. In your listener/visitor for for multiplication expressions, it's up to you to decide if this is an error (and the resulting "type" is InvalidType, perhaps), or maybe, like Ruby, it's a cute way of getting "stringstringstringstringstringstringstring", in which case you'd return a type of String. For functions you have decisions to make about the return type of the function. Do you require them to be explicitly defined? Must the be defined before they're referenced (if not, you'll need to make a pass of you parse tree creating a symbol table of functions and return types to reference, before you can navigate your tree evaluating expression types). Maybe, you have a dynamic language where different input types (or even values) might result in different return types from your function.
Clearly, this gets pretty deep into language design choices, and languages have made many different decisions about how to handle them. ANTLR is just your parsing technology and (other than providing convenience classes like listeners and visitors) has nothing to say about how you make these decisions or how you implement them. And, there's not a way to codify them in your grammar as they ares semantic concerns that have no impact on parsing or the construction of your parse tree.