IMO, good use of both Inheritance and Composition with Nodes allows you to do pretty much anything you might accomplish with Traits. Itās also allows you to be very strict with type-checking if thatās your thing. (Itās my thing. I turn on warnings for missing strict typing.)
As Iāve learned more about how Godot works, Iāve learned about the power and flexibility of Nodes and Resources. Like most people who come from other languages and have a lot of programming experience, I spent the first year or so trying to optimize things that didnāt matter, by using RefCounted and Object classes instead of Nodes and Resources. At this point, I could probably write a book on all the pitfalls of coming to Godot and GDScript with outside experience.
An example of this is learning curve is the difference between my Camera3D Plugin which I initially architected last April, and my Camera2D Plugin, which I started this January.
Camera3D Plugin
My Camera3D Plugin has two classes, Cameras and CameraMount.
The Cameras node sits as a child of a CharacterBody3D node and handles multiple camera angles for a player. It responds to a change_camera Action that the plugin creates (and deletes if you uninstall it), switching between each Camera3D or CameraMount3D node that you attach to it.
The CameraMount node contains a SpringArm3D, Camera3D, and two Node3D pivot nodes - which are used to prevent Gimbal Lock with Euler Transforms. It handles everything for a default 3D 1st-person or 3rd-person camera. (All input is handled by my Controller Plugin. It supports both mouse/keyboard and gamepad input seamlessly.)
When I created this, I was focusing on inheritance and having as few nodes as possible. I also wanted it to require no work from me to use. I didnāt want to have to recreate camera controls every time I made a new 3D game. I originally intended this plugin to be for 2D and 3D - a result of which is that I forgot to change the addon folder name from dragonforge_camera to dragonforge_camera_3d. (I prepend ādragonforgeā at the beginning of all my plugins to prevent namespace collisions - a habit from making Java Maven packages.)
At the time, I basically wanted an object to manage all the cameras - but being against naming things āManagerā, I called it Cameras - as it is a collection of cameras. Then all the cameras were hung beneath it. Originally, I exported a list of cameras, but I found that was just extra work - and at least for my current uses, overkill. So I changed it to look for Camera3D and CameraMount3D child nodes when it is made ready. (I would now change that to check them entering or leaving the tree instead.)
Camera 2D Plugin
When I started making this plugin, I had a version of a BoundCamera2D from a game Iāve been working on since last year. That camera looked for a signal from a TileMapLayer and bound the camera limits to the used tiles in it. It also handled zooming, panning, and following multiple characters. As I started building the plugin, I realized that I wanted to have multiple camera functions and I ran into an issue of multiple inheritance - a problem that could be solved by traits. But I realized, that Godot already had a solution. Composition through nodes.
When I created my State Machine Plugin, I learned a LOT about how the Godot Node system works. I had taken a Resource-based state machine from a C# tutorial, and decided to remake it in GDScript. As I did, I realized it made a lot more sense to make it Node-based.
It was a lot easier to see and edit in the Godot editor, and it didnāt really take up that much more memory. While it made it a bit more complicated to add and remove states while the game was running, I was able to leverage the Node functionality to do a lot of heavy-lifting. It also allowed me to make a pull state machine (based on the Kanban lean manufacturing and Kanban software development processes).
I learned how to handle nodes entering and leaving the tree, activating and deactivating processing and inputs, and how to wait for parent nodes to be ready. (Nodes are created and enter the tree top-to-bottom, but made ready bottom to top.)
So, I decided to just make all the added functionality of the Camera2D as nodes that could be added to a Camera2D. In this way, if someone wanted to have their own custom Camera2D, they could add functionality by just adding a node.
So now I had a Camera2DLimit node that could take a TileMapLayer as an @export variable, but could also programmatically take a TileMapLayer or Rect2i to do the same thing. I added zoom functionality for mouse, keyboard and gamepad in a Camera2DZoom node. I added pinch zoom support in a separate Camera2DPinchZoom node. I added panning with edge scrolling, keyboard and gamepad, plus touch screen two touch scrolling all in one Camera2DPan node. I even added camera shake, with optional controller vibration as a Camera2DShake node.
While I was at it, I learned how to make warnings appear in the editor, so it was clear that they had to be added to a Camera2D directly. All of my composition nodes inherit from Camera2DComponent, which handles the warnings, and tracks the Camera2D node it is attached to.

Ultimately, I used composition over inheritance to allow for inheritance for the Camera2D node at a later time, but I also used inheritance in my composite nodes.
Camera3D Plugin - Changes I Would Make Now
If I were to redo this plugin - which at this point I have no desire to do so (because it works) - I might prefer composition over inheritance more. I would make the CameraMount3D node into two - one for first-person and one for 3rd-person cameras. When I first made this plugin, you couldnāt add custom nodes from the Add Node dialog. I also couldnāt find enough camera icons I liked. Now Iād separate them out because you may not need both.
I would also programmatically create the CameraMount3D node so I didnāt have to store a separate scene, and it could be added through the Add Node dialog. I learned how to do this when making my Curved Terrain 2D Plugin and Curved Text 2D Plugin (which I have not made public yet).
I also might make the Cameras node less reliant on cameras being direct children of it, and instead search for anything attached to the CharacterBody3D node. I might also add in an @export variable again as an optional way to add more cameras. Say you have fixed level cameras like Metal Gear Solid - you might want to use those instead of something attached to the CharacterBody3D.
Alternately, I might consider making it an Autoload, and then let it register all Camera3D nodes as they load in the game.
Conclusion
The Godot way of doing things is much more flexible than people think when they first start using it. The team has done an excellent job of making GDScript more performant than C# in most cases. So using it as-is, is often the best way to reduce memory usage and increase performance - rather than trying to tinker with things underneath. Most of the tasks that are more performant in C# and C++ are math related - but not math related to physics or graphics - in those cases GDScript is often the winner.
Godotās Node and Resource objects are very performant, and offer a lot of the flexibility people think they need, but donāt know they already have.