← Back to Blog

@pyrpc/types: from type-only to runtime dependency

·6 min read

For most of pyRPC's life, @pyrpc/types was what its name suggested: types. It exported interfaces, which compile away to nothing. A type-only dependency. v0.12.0 changed that in one move, and the change is visible in the adapters' package.json.

The change

// @pyrpc/react package.json
"dependencies": {
  "@pyrpc/client": "^0.12.0",
  "@pyrpc/types": "^0.12.0"    // ← moved here
},
"devDependencies": {
  "@pyrpc/types": "*"          // ← was here (dev-only)}

Moving @pyrpc/types from devDependencies to dependencies is a one-line diff with a heavy meaning: the package is no longer consumed only by the compiler. The adapter does import { procedureKinds } from '@pyrpc/types', a value import. Consumers need it installed as a real, runtime-visible dependency.

Why npm cares

devDependencies are not installed when a package is installed as a dependency of something else (they are for the package's own development. If @pyrpc/types stayed a devDependency of the adapter, an app that installed @pyrpc/react would not get @pyrpc/types at all. Type-only imports could get away with that (types resolve during the app's own compile). A runtime value import cannot) the specifier must resolve in the app's bundle, so the dependency must be declared where consumers can see it.

The version coupling

The dependency is range-pinned: ^0.12.0. pyRPC releases all packages in lockstep, and the release script sweeps every @pyrpc/* dependency range on every bump. The range means the adapter's hooks and the types package can never drift to incompatible majors within a release train, a guarantee that matters more now that the relationship is a runtime contract rather than a compile-time suggestion.

The devDependency stays

The adapter keeps @pyrpc/types: "*" in devDependencies for local development and testing, the workspace-local package. Both declarations coexist deliberately: dependencies states what consumers get; devDependencies states what the adapter's own build and test environment uses. The release script keeps both in sync.

The signal

Dependency placement is architecture documentation. When a package moves from devDependencies to dependencies, it is announcing: this is no longer erased at compile time, my runtime depends on it. For pyRPC, that single move captures the whole v0.12.0 thesis in the most boring file in the repo.