Given that is deterministic, it should be possible to just run the code and record the set of types that reach every parameter and variable.
A bit expensive, perhaps. And it breaks down if the code is in an intermediate state. Like, "I just deleted a comma, where did my type annotations go?"
Are you saying that every function can only be called with a consistent set of types in the parameters? It’s not possible to call a function two times and supply parameters that have different fields on them?
Unless that is true, the end result might not be ideal. You could certainly record the information that is there at every line of code at runtime, and you could probably calculate the union of the parameter types to find only the fields that are always there, which would be fairly okay I guess.