QML is fundamentally based on inheritance. Each component (except QtObject) derives from another component, and makes all members of the derived-from component available as its own. This paradigm has well-known limitations. In particular, If you have a member that shows up in multiple branches of the type hierarchy, and you want to access it in a unified way from a different component, you have three possibilities:
- Switch between all the branches at the place of access, using "as" or "typeof". This is ugly and requires you to know all the possible branches in advance.
- Add a meaningless member of the required signature to the last common ancestor of all the branches. This is ugly, wastes memory, and increases complexity.
- Duck-type the access. Just write foo.bar and the lookup mechanism will take care of it. This is expensive as you will constantly invalidate the lookups. It can also not be compiled to C++ in an efficient way because we don't know the return type of the access.
So far, people have predominantly used duck typing as the solution for this, because the code looks neat and the slowdown will only manifest once one of those lookups ends up in a hot code path. This is a problem for the QML-to-C++ compiler.
The slowness of invalidating lookups is hard to prevent, unless we allow the system to store and select between multiple alternatives for a lookup. However, that's the same for interpreted and compiled mode.
The real problem is the fact that we don't know the return type of the access, and neither the argument types if it's function call. A compilation to C++ would therefore need to operate on generic types like QVariant after the access, and that is slow and complex.
There is a solution to this. We might declare the expected signature at the place of access (or elsewhere). We might allow "empty" function declarations as "interfaces". The code might look like this:
The function signature declaration is quite a mouthful, but it could be shortened using arrow functions and it could be factored out. We might allow using any completely typed function as "interface" for casting other functions.