For the past few weeks, my main goal in my open-source contributions was to work on tools that real TypeScript developers use every day, and to challenge myself with issues that were not just styling fixes. I picked two projects. One was typescript-eslint, which is a big set of ESLint rules that help keep TypeScript code safe and consistent. In that project I focused on the no-unsafe-* rules that try to stop the any type from quietly spreading through a codebase. My second project was publicodes/language-server, which is a VS Code extension that gives language server features like diagnostics, completion, hover and go to definition for the Publicodes language. Together these two contributions let me work on both static analysis rules and an actual language server.
In typescript-eslint I worked on a feature request that asked the no-unsafe-* rules to catch unsafe any values inside object types, not only at the top level. The rules like no-unsafe-argument and no-unsafe-assignment already blocked obvious cases where a parameter or variable is typed directly as any, or where a whole generic type uses any. The problem described in the issue was that if you had an object with a nested any property, it could pass into a stricter object type without any warning. I changed the internal helper that these rules use so that it can now walk into objects, arrays and other containers to see if there is an unsafe any hiding inside. I also added tests to prove that the exact example from the issue is now reported, and that a safe version still passes. Finally, I had to clean up a few parts of the repository that started failing under the stricter checks. My PR
In publicodes/language-server I worked on support for multiple models in one workspace. The language server integrates publicodes language into editors like VS Code and offers features such as semantic highlighting, diagnostics, and go to definition for .publicodes files. The issue was that the server treated all .publicodes files in a workspace as if they belonged to one giant model. That was a problem in monorepos or multi folder workspaces, where you can have several separate models that should not see each other. My fix was to introduce the idea of a model boundary, for example the nearest package.json or .publicodes.config file, and then group files by that boundary. Each group gets its own internal data structures and engine instance, and all diagnostics, hovers, completions and definitions for a file now go through the model that owns that file instead of a global one.
I did not build any of this in one sitting. A lot of the work was just slowly reading through code that I did not write and trying to understand how things fit together. In typescript-eslint, despite already working on this project, I spent a long time inside the type utilities before starting writting code. I ran the test suites many times and kept adjusting my changes when I saw new failures. In the language server I had to trace how files are discovered, how rules are stored, and how diagnostics are produced, and then carefully thread the new model information through those paths. This forced me to break the work into smaller steps, for example first tracking models, then updating parsing, then updating validation, instead of trying to rewrite everything at once.
Looking back at my original goal, I think I did achieve what I wanted. I stayed in the TypeScript tooling space, I picked tasks that were slightly above my comfort level, and I stuck with them even when the logic became hard to follow. I also saw the downside of picking complicated issues, which is that it is easy to overthink and get stuck, but overall I feel more confident now about working in serious open source projects. For future work I would like to improve how I plan these contributions so I leave more time for reviews and polishing, but I am happy that I pushed myself and produced contributions that feel real and useful.
