I have a very unorthodox setup for my current project. The project consists of a django application with multiple “apps” or folders that try to containerize the logic of different sections of the website. I wanted to introduce Typescript into the project, since I love the benefits of typesafety.
However, since this is running purely on Django (no framework running in parallel to the backend, just scripts included in an HTML script tag), what I opted in to do was to just create Typescript files, transpile them and then use the resulting JS files in the HTML script tags required. Each app or folder has a “static” folder, with two subfolders inside: “ts” and “js”. Beside those two folders, there is a tsconfig.json with the following config
{
"extends": "../../../tsconfig.json", // Path to global tsconfig.json
"compilerOptions": {
"outDir": "js", // Where the transpiled Javascript files go
"rootDir": "ts", // Where the Typescript files are
},
}
This helps the compiler know that I want the files in the ts folder to be put in the js folder. Django then simply picks up the resulting JS files and uses them.
As you can see, the config uses a parent configuration with the same name, that contains the following:
{
"compilerOptions": {
"target": "es5",
"module": "ES2015", // Include imports/exports in the transpiled Javascript
"moduleResolution": "node", // Use the locally installed Node modules
"lib": [
"es2019",
"dom",
"DOM.Iterable",
"es2017"
],
"sourceMap": true, // Generates a map file that allows us to see the "non-minified" code when debugging
"noUnusedParameters": true, // Warns when a function parameter is not used
"noImplicitReturns": true, // Enforce always adding types to function inputs
"removeComments": true, // Remove comments from the transpiled JS file
"strict": true, // Enable all strict type checking options
// Include the globally installed modules
// (Check where your global modules are installed with: npm root -g)
// NOTE: The first type root is used by the node_opus container. The
// rest you can use them to point to your locally installed global
// modules.
"typeRoots": [
"/home/node/opus/nodejs/node_modules/@types",
"./nodejs/node_modules/@types"
],
// Specify "types": [] to disable automatic inclusion of @types packages.
// https://stackoverflow.com/questions/47303252
"types": ["jquery"]
},
// Exclude the "node_modules" folder from the transpilation process
"exclude": [ "node_modules" ]
}
Everything good up to here. Scripts compile into JS files and files that are next to each other can be imported really easily with proper linting support:
import { APIError } from "./types/common.js";
However, one of the errors that I have found is that when I try to reference a Typescript file found in a different app, like in the following example
import { OpusProgressBar } from "../../../../static/app_core/js/components/progressBar.js";
I get the following error: Could not find a declaration file for module "X". "X" implicitly has an "any" type. This is a pretty big deal breaker for me, since this removes type annotations from my imports, which was the reason I introduced Typescript in the first place.
I tried looking around at how to fix this, and while trying to do this, discovered a few things:
- When I go to the transpiled Javascript file, that file actually has proper type annotations.
- Some posts mentioned the fact that I need to use a
.d.ts file to solve the problem. I created a sample progressBar.d.ts and included it in the parent tsconfig.json file like you can see below. This caused all imports in my files to be automatically fixed. With them getting correct linting support. No idea why this would work
{
"compilerOptions": {
...
},
// Exclude the "node_modules" folder from the transpilation process
"exclude": [ "node_modules" ],
"include": [ "progressBar.d.ts" ]
}
- When the previously mentioned thing worked, I tried compiling, and it failed. It told me that the include setting in my
tsconfig.json did not include the files that I was trying to compile. So I added an entry for my scripts in the include setting like this [ "progressBar.d.ts", "./app_name/**/ts/*"]. This made the compilation work again, but my imports broke again
- I have jQuery installed in my npm global modules, and I use the
$ symbol associated with jQuery in multiple scripts for my Django project. Interestingly, for that specific package I do have linting. However, when I fixed my imports, the linting for it just went away
Im sure all of these “symptoms” might sound familiar to someone out there who might be able to help me out. Maybe someone can help me out so that I can have a setup with both correct linting and proper compilation behavior. I just want to code and not have to worry about this tbh. Thank you!