It feels like you misunderstand nix’s value proposition and methodology.
nix alleviates certain reproducibility concerns by defining each package (in nix terms, “derivation”, which can deliver binaries but also other files) in terms of it’s dependencies (in nix terms, “inputs”). That means that pkg-3.2 that depends on foo-1.0 that depends on baz-0.5 is not the same as pkg-3.2 that depends on foo-1.0 that depends on baz-0.6. If any dependency changes, it is a new package from nix’s standpoint. Knowing that a given /nix/store/<hash>-<pkgname>-<ver> specifies it’s dependency chain like this is the core value proposition of nix. pkg-3.2 is not a useful specification from the perspective of nix.
If you change any dependency, then the downstream derivations will change, requiring them to be rebuilt. That is the cost of the input-addressed method.
nix is a source-based distribution, but it has a binary cache which allows certain builds to be substituted instead, improving convenience and bit-for-bit style reproducibility. It’s just not possible to build every combination of every possible dependency and resulting downstream packages. The build and storage for that would scale exponentially. Therefore, a tractable approach is to have specific versions that get evaluated and built together, then cached. It’s an imperfect solution, but one that scales (better).
You really can’t compare TypeScript lockfiles at all - a single dynamic language is not a good comparison for a way to do something similar with packages written in any possible language and compiled, linked, and resolved in multiple different ways.
I get that you may not like the compromise that nix and specifically nixpkgs makes in this regard, but you have the freedom to override definitions and build yourself if you want to. Take for comparison Arch’s pacman, and getting old versions is hard enough – and completely forget trying to specify a group of system packages easily and ensuring they don’t conflict with other packages on your system. Arch is another system that also does not really believe in partial upgrades, but the difference is that they tradeoff ability to have input-addressed reproducibility and concurrent deployments of conflicting packages for ability to quickly deliver packages and reduced build/storage costs. It’s a reasonable trade, depending on your needs. Every technology is a series of tradeoffs for use case. If nix serves your use case, great! If not, also totally cool, and I hope you do find a suitable technical solution for your problem domain.
I’ll also say that I prefer to keep my system dependencies pretty lean. Instead I write project-specific shells and packaging for specific projects. When I stop working on that project, I’m fine with the /nix/store files to be garbage collected, knowing that the definition files will still allow me to recreate the dev environment or project-dependencies in the future.