Check out a free preview of the full Production-Grade TypeScript course:
The "Viral Options" Lesson is part of the full, Production-Grade TypeScript course featured in this preview video. Here's what you'd learn in this lesson:

Mike explains why the allowSyntheticDefaultImports, esModuleInterop, and skipLibCheck cause type information to be leaked out of a library and should be avoided. This segment demonstrates alternative methods for importing code from modules without enabling these properties.

Get Unlimited Access Now

Transcript from the "Viral Options" Lesson

[00:00:00]
>> So, the last little thing I want to talk about in this retrospective of what we've just done in this little library setup is the things that I referred to as viral options. And those are, they're disabled. So it was sort of scanning for them. They're these two and this skipLibCheck.

[00:00:27] So let me move these cuz, so they're all together. These three, I do not like. So let me explain what they do and why I feel you don't need them. AllowSyntheticDefaultImports is all about CommonJS and ES module interrupt. So if we had something like this, Let's say it's a JS file, are we allowing JS?

[00:01:06] We're gonna have to turn this on, oops. So I'm saying, allowJs. So here if I said function product a and b, return a*b. Let's document this nicely. All right, so if this were Node, you do this. And it's not liking this because we'll tell ES led to Jump in a lake here.

[00:01:59] All right, so we have a CommonJS module here. Now if we wanted to use this elsewhere like in our subtract module, we can go up here. If we had ES module interrupt turned on we could do import. Actually this will work in both cases So this will be fine, and there's our method, right?

[00:02:26] The type information is coming through, we're not using it, okay, now we are. So this is fine, because this is like a named export, this is where CommonJS and ES modules agree. I've got a collection of things and I'm making them available to the outside world. Now, what if we had a bunch of functions here?

[00:02:47] Like, has anyone here, show of hands, who's used the FS module in Node.js, like for reading, writing to a file, a lot of people, great. So, I might wanna use like 10 things from that module. I don't wanna import each of those independently. Maybe, I don't. Let's say, I don't.

[00:03:10] So, I could grab the whole thing. As a namespace, this is called a namespace import. Now, what ES module interrupt would have let me do is this, it would convert this kind of export as if it were sort of export default. I would rather not, in fact consumers of my library, cuz I have a workaround, it's not that bad.

[00:03:42] It's a namespace import, no big deal. Where things get more interesting is this, All right, this is really nasty. My CommonJS module is exporting one thing but it's clearly not a namespace, and you can even see here like this error message unfortunately is telling me I must turn this flag on, or else I must turn it on.

[00:04:15] But being a stubborn developer, I'm gonna ignore that cuz in fact you can do this. Note that it is not a const declaration. Actually, I should rename this at this point, [LAUGH] Cuz it's not a namespace anymore. So it's not a const declaration it's import product = require.

[00:04:44] Good luck finding this in the documentation. It works, it totally works. And it's a solution to this problem that does not require that I leak this type information out into consuming apps. I can insulate them from this interrupt challenge. And at least in Node and most run times this should work just fine.

[00:05:08] A little ugliness but a small price to pay for making sure you don't expose other people to these types. You don't want them having to turn this flag on. And an important realization you have to arrive at in order to understand why this is important is a type checking.

[00:05:28] It is a holistic and global operation. So your apps types are analyzed along with all the dependency types and their dependency types, everything's all solved at once. It's almost like a big linear algebra problem where we're trying to like solve like a system of equations with like matrix inversion, right.

[00:05:51] Everything is done at once. So if your library needs to do weird stuff in order to compile, it's likely that consumers of the library will need to do that same weird stuff. You want to protect them from that. You do not want to turn this flag on. If your types only compile when that flag ES module interrupt is turned on, everyone who uses your library will have to do the same and it just sort of seeps out, but this is fine.

[00:06:20] And yes, I am telling you to take on tech debt, before anyone asks that question. This is not something that has a very clear path forward. However, I balanced that with the need for libraries to focus more on compatibility compared to AppCode, right? If your library says, I support all stable versions of Node, well, you'd better be testing it against Node 10.

[00:06:49] At the time of recording this course, that's the LTS, the oldest version, it has to keep working there. In fact, we'd probably better build with that version of Node. That's our most challenging runtime to maintain compatibility with. So libraries have to be a bit more conservative about how eager they are to adopt new things like whatever replaces this, which frankly is gonna be modules that look like this.

[00:07:21] The solution to this is we just don't use CommonJS anymore, we use ES modules in Node. But we're a long way off from that. There's a lot of CommonJS code out there. So, what's important is that you understand how this works. Now, let me show you the other side of this.

[00:07:39] If we were writing this in a more modern way, let's say that this legacy thing was a TypeScript file. This will really throw you for a loop. So, and I'm not showing you this just like for weird trivia. This is stuff that hangs up, cuz it's hang ups is a lot of people like, how do I deal with this?

[00:08:00] How do I type this correctly? I've answered this question countless times with helping people at LinkedIn. So now I've just added types here. So we've converted this legacy.js to legacy.ts and I'm getting like Unsafe member access. It really just does not like module here but we can just do this.

[00:08:25] Another weird thing that you won't find in the documentation Export =, that is the equivalent of module.exports =. So I've just taught you a way to make CommonJS work. Either exploiting a namespace like you'd find with FS, in which case this here, oops, sorry, I'm modifying the same file in two buffers.

[00:08:54] For fs, this is fine. This is fine, Because it's a namespace that you're consuming. It's not just a function in and of itself, but occasionally you will end up having to consume something that is not a namespace. This is how you type it in terms of the export.

[00:09:19] This is how you consume it on the other side. And if, just to come full circle, if we just done this, now we can go back to this. Actually in this case, it's even easier. Now you can go back to this. There is my product. So the only time you get to that serious weirdness here, the thing you never seen before, that is certainly not JavaScript.

[00:09:49] It is if you have a CommonJS module whose single export is a non-namespace value like a callable value, that is the weird, edgy, it's like a quarter of an edge where you have to use this weirdness. Not often, but man, this throws people for some serious loops if they can't identify the way to type this properly.