Solution with a bundler:
Your scenario seems like a good use-case for a bundler. This is a tool which analyses your code and draws in all dependencies to produce a single output file. Popular choices are webpack, browserify and parcel.
This means that the dependencies imported from A will be included in the output file generated for your public scripts.
Parcel has a "zero-configuration" philosophy, which means that (very much different from webpack or browserify) it will work with no setup required.
I recreated your code:
I have parcel installed globally (npm install -g parcel), so I can just run:
parcel build src/to_public/*
This looks at your to_public folder and bundles all scripts inside. Parcel is aware of typescript and automatically calls the typescript compiler. The resulting scripts are copied to a new dist/ directory. Your folder now looks like this:
For the files outside of your to_public folder, you want the full support of typescript, but you don't need to compile them. To do this, you can add the noEmit flag to your compilerOptions.
{
"compilerOptions": {
"module": "commonjs",
"noEmit": true
},
"exclude": [
"node_modules"
]
}
Parcel has some very nice features. For example, it caches the compilation and is quite fast if you call it repeatedly. Also, it interfaces nicely with other tools from the Javascript ecosystem. The interop with the typescript compiler is seamless. Parcel will use your settings from tsconfig and even produces sourcemaps by default.
Of course, strong cases can be made for the alternatives, too. I'm recommending parcel here, because it comes at basically zero cost. You just install the package and without any config, it simply works.
Solution without a bundler:
If you want to avoid a bundler, you could use multiple compiler configurations. The problem is that typescript needs to know about the interfaces you are using. It can do this by looking at the sourcecode, or by reading a definitions file. So your answer is a viable solution but it would be nice if the definitions were created automatically. I have set up three compiler configs:
- for general syntax checking
- to produce the definition files
- to compile the public javascript
The default config is this one, it will be picked up by your editor. I have set noEmit to true, to avoid accidental compilations that would mess up your folder structure.
tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"noEmit": true,
"rootDir": "src"
}
}
Then I have split your sources directory into src/server and src/to_public. Without this split it's hard to exclude files from the compiler (you have to list them manually).
Here is the config which produces the declaration files. It has the flag emitDeclarationOnly and the output directory is your public javascript folder:
definitions.json
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"emitDeclarationOnly": true,
"noResolve": true,
"rootDir": "src",
"outDir": "src/to_public"
}
}
If you run the config above, it will produce declarations from your server-side code. This can be used for the last compiler config. It reads the files in your to_public folder and compiles them to the output directory. The important compiler flag here is noResolve, this will avoid copying the declaration files to the output folder.
public_scripts.json
{
"compilerOptions": {
"module": "commonjs",
"noResolve": true,
"rootDir": "src/to_public",
"outDir":"public/"
},
"exclude": ["src/server"]
}
You can now compile your code with
tsc -p definitions.json
tsc -p public_scripts.json
Please note the import paths. If you want to use server/A.ts in to_public/file.ts, then you import "./server/A.ts"