Intermediate TypeScript Modules & CJS Setup
Transcript from the "Modules & CJS Setup" Lesson
[00:00:54] As a result, several community based solutions emerged. We have CommonJS which was popular in the node community, AMD and UMD and these were more popular for browser based apps. Because of all the bundling technology we use for browser based apps, these two types of modules AMD and UMD have sort of gone away, right?
[00:01:21] Very few people write code like that, although sometimes we have these as a compiled target we don't really see that code. Common JS has stuck around and it's largely as I said, because of node, right. Node still consumes common JS libraries. And the core libraries of node are written in this format, so we need to know how to consume them.
[00:01:47] It's important to understand that I'm assuming and I encourage you to only write ES modules these days. You should be writing your own new code, using the new standard. Because that's a good way for you to be forward compatible, right? That your code will have a longer shelf life.
[00:03:57] Well first, if you're writing code that looks like this, or if If you're looking to write typed code that does the same thing as this, often you can just use this namespace import, right. So when we say I'm gonna grab this namespace fs from this module, if you've ever used this fs module which is one of nodes core modules, it's just a collection of functions hanging off of this fs thing.
[00:04:23] There's like read file sync, right file sync, read JSON, write JSON and the fs is just sort of a way to hold them all together. And we can import it just like this. Or you could import using a named imports like specific things from fs. Almost all common JS interrupt can be handled in this way.
[00:04:48] But there's one case that will really throw you for a loop if you encounter it and that's the case where whatever the module is exporting as its single export, it is not something that looks like a namespace. So let's look at this case here, where we have a function and effectively in our common JS code, we would see something like this, right?
[00:05:15] module.exports equal In this whole function. Now we can't represent a function as a namespace, and by the way this is the equivalent code in TypeScript to accomplish this here. So if we try to consume it in this way, we're going to get an error message. And this says that if we wish to use ACMA script, imports and exports with this module, we have to turn a compiler flag on called esModuleInterop.
[00:05:45] And this error message is telling the truth. But what it's not telling us, is that there's an alternate solution to this problem and that is, for us to use an import that is not aligned with the ACMA script standard. And this will save us from having to turn this flag on.
[00:06:04] Why would I want to leave this flag off? Well, this is what I call a viral option. So viral options like es module and allowsSyntheticDefaultImports, if those are necessary, if you have to turn those on in order to make your types work, everyone who consumes your types will also have to turn them on.
[00:06:28] So particularly when I'm writing library code, I jumped through some serious hoops to make sure that I leave these off. And that means that if you're using one of my libraries, it's your decision whether you want to enable these or not. I don't want to force that decision on everyone who uses my code.
[00:06:48] So I call these viral options because when one library enables them, anyone who consumes it also has to turn it on. Thankfully, we have another solution to this problem, right. And that's the non ECMO script import. Here is how it works. So we still have this code here remains the same as when we last saw it.
[00:07:10] But here is the key thing, the key idea. This looks kind of like a common JS Import. But one words different, right? We're used to saying seeing const createbanana = require(" /fruits'), but we see import instead of const. This is what makes it kind of a unique thing, right?
[00:07:32] It's the imports used to be this way in very old versions of TypeScript, and you can still use them. And you can see the type information still comes along for the ride here. So given that in this situation we have to choose between a bunch of non ideal possibilities either forcing consumers of our code to turn some compiler option on, or doing this, I would sooner do this because I like the idea as a library author, I think it's part of my job to like absorb some of that pain for downstream consumers of my code.
[00:08:09] So I will almost always do something like this compared to forcing people to change their compiler settings. If we look at what this code compiles out to, thankfully, it's exactly what we would want. We can see module.exports = createbanana. And if we look up, that's what we were trying to accomplish here.
[00:08:58] Just on the topic of making sure your types don't require special compiler options, a quick reminder that VS code will download type information in the background as it sees users consuming a library. And there's a possibility that even if they're not depending on your library's types like directly, they'll start to see some autocomplete information in their authoring environment.
[00:09:26] So it's important especially if you're doing open source work, it's important to be good citizen. Make sure that if you publish types, those types of work. And make sure that you don't spread those viral options, and put that burden on your consumers cuz it may be a very tricky thing for them to straighten out, if they start to see red squiggles under their code cuz TypeScript is unhappy.
[00:10:22] So here let's imagine we have a react component that needs an image file, well, you could import an image like this. Here's a link to the Webpack documentation where I copied this line from. Now TypeScript is not happy with this by default. It's saying, I can't find a module named file.png, and that's because there is no TypeScript module called file.png, not in this environment.
[00:11:13] So in plain language, this could be taken to mean, I hereby state that a module exists and the name of that module is some arbitrary text followed by .png. And the default export of this module is going to be a value whose type is string. As a result of having this little piece of type information, elsewhere in your project, you're gonna see that this exact same line that was failing before, it's now not only error free, but we can see that the value of this IMG thing, it is of type string, which is great, that's ripe that's ready to put into an image tag source equals this, right?