Static Analysis: Shockingly Useful! ⚡️

Introduction to Static Analysis in Coding

Josh Goldberg begins his presentation by discussing the significance of static analysis in coding, addressing common fears, uncertainties, and doubts developers have about using tools like linters and formatters.

Understanding Static Analysis

Goldberg explains the concept of static analysis, emphasizing its role in scrutinizing code without running it and contrasting it with dynamic analysis.

The Spectrum of Static Analysis Tools

He introduces the spectrum of static analysis tools, ranging from formatters like Prettier to type checkers like TypeScript, and discusses their varying speeds and functionalities.

Role and Benefits of Code Formatters

Goldberg highlights the role of formatters in code standardization, discussing how they improve readability and consistency in codebases.

Understanding Linters and Their Importance

He shifts focus to linters, explaining how they perform discrete checks on code and how they differ from formatters in their function and purpose.

Emphasizing the Role of TypeScript

Goldberg emphasizes the significance of TypeScript as a tool for enhancing code quality and safety, explaining its role as a superset of JavaScript that includes type checking.

Combining ESLint with TypeScript

He discusses the combination of ESLint with TypeScript, exploring how this integration can provide a more robust and effective static analysis environment.

Concluding Thoughts on Static Analysis

Goldberg concludes his talk by summarizing the importance of static analysis tools like formatters, linters, and TypeScript in modern coding practices. He encourages the audience to adopt these tools to improve code quality and consistency.

What a great introduction!

All right, last main series talk of the day before with the ending panel.

Feeling good?

Y'all feeling good?

All right, y'all feeling fearful of the linter?

Hopefully not soon.

That's the alternate talk title, Don't Fear the Linter, because I thought of this a couple weeks ago and I thought that was cute.

This is basically my full time job, just linting everything.

Love it.

But hey, everyone, I'm Josh.

Thanks for having me.

I want to talk to you about emotions today to start off.

But not these emotions.

These emotions.

Specifically, fear, uncertainty, and doubt.

Whoa, my transitions are off.

Okay.

Anyone hear this before?

FUD?

Fear, uncertainty, and doubt?

Fear.

I have read editor squigglies.

Something is broken.

I'm afraid, oh no, uncertainty, I have not read the error message to find out what's broken, I just know it's broken and I'm not going to learn more, I haven't read the docs, and doubt, I know my code works, the editor is wrong, the tooling is wrong, this disgusts me, I do not like it, and I experience it, that is me, on a daily basis, no more of that, no more FUD, don't FUD it up, instead, we are going to use Static analysis, with love, and appreciation, and happiness.

Because you will realize that static analysis is actually pretty cool.

And when you use it a lot, you'll realize that before you fully utilized your linter, and your formatter, and your type checker, it was like you were wearing sunglasses indoors.

Which is useless, and stupid, and just a waste of yourself.

Star eyes, okay.

Hi everyone, thanks for attending my talk.

But what is static analysis?

This is a term That is not commonly taught in uni or bootcamp or etc for everyone.

It's the, to sum it up, it's the thing that gives you the squigglies.

For non technical people, when I explain what I do, I say, oh, it's like the grammar checker or the spell checker in MS Word or Google.

Static analysis is tooling that scrutinizes your code without running it, which is in contrast to dynamic analysis, tooling like tests that run your code and then look at the result.

So static does not move, dynamic does move, hence the ZAP emoji.

If you haven't gotten it by yet, you're going too slow.

And we do have a spectrum of different static analysis tools, similar to how in dynamic analysis you have a spectrum of speed with unit tests to scale with end to end tests.

For us, our spectrum of speed starts with formatters, who here uses Prettier.

Woo!

Yes, almost everyone.

I'm so annoyed at the two hands that didn't get raised.

On the other end of the spectrum, we have the power, the type trackers, who here uses TypeScript.

Slightly fewer hands, okay, alright.

And in the middle, we have traditional linters, who here uses ESLint!

I like the preemptive hands raising.

Yes!

Woo!

Love it.

So we're going to dive in.

And I'm going to go through each of those things.

I'm going to belly flop on all of them.

And my hope, my sincere, genuine hope, is that if you hadn't yet known what each of those three things does, by the end of this talk, you will know generally what they will do and how to get started in them, along with kind of how they work.

Hooray!

So let's talk with, the simplest of them, with, quotes whenever someone says the word simple in programming.

What is a formatter?

And yes, this is the whole, tab spaces debate.

A formatter is a tool that reads in your source code, and reformats it, as you might have guessed from the title.

It does not respect most of the formatting preferences you came in.

Your code might have mixed tabs, spaces, semicolons, no semis, whatever.

It just reads the code in and reformats it out for you.

For example, if you have this weirdly formatted thing, this is valid JavaScript, despite how it looks.

It would take that in, and it would output friend equals friend or me.

The exact same code and runtime, but more easy to read.

And the way it does it is by using, has anyone heard of a static analysis AST?

Abstract Syntax Tree?

Nice, like half the room, love it.

The look of death was much more common in the eyes of people raising their hand.

An AST, or Abstract Syntax Tree, is an abstract, so general, representation of your source code.

Most ASTs, including the formats and all the tools I'll show you, can generally be serialized to something like JSON.

And in this one, you don't have to memorize what's in it.

We see that it's an assignment expression.

Do I have the, I forgot to check if I have the little laser pointer friend.

Do I have the laser pointer?

I'm not going to try right now.

This would be a foolish.

Yeah, there we go.

Yeah, so this is an assignment.

It's something equals something.

And on the inside, it's, we're assigning friend equals.

And on the right is a logical expression, friend equals friend or me.

AST!

Great.

And an AST is the same type of thing, same concept, using all of the static analysis tools and even some dynamic analysis tools.

It's a general way to represent your source code.

And that's all I'm going to talk to you about ASTs.

We don't need to know them deeply.

It's nice to know that Oh, an AST is the format if you ever see it in docs, but you don't need to be a super expert on it.

It's okay.

You don't need to fear it.

Just remember that's AST.

If you want to get started with a formatter, I highly recommend it.

Prettier is the most common JavaScript one.

Most hands in this room were raised, so great preaching to the choir here.

Npm install Prettier as a dev dependency.

Oh, I keep forgetting to fix the typo with the dashes, that's annoying, and then npx prettier dot, so the current directory, dash, we'll rewrite all your files with the default prettier formatting.

Has anyone ever run these exact commands but correct?

Just curious, okay.

My beard hair is triggering.

Okay, you don't have to remember those commands.

I have a template repo with all the things set up if you want- template-typescript-node-package.

It's got a whole bunch of stuff to set up a new repo off the template or hydrate an existing one.

It's also a great good first issue repo.

If you want to get started on open source, hit me up.

And also, all the slides are on my Twitter, and I will soon put on my foster daughter, my blue sky, Joshua K.

Goldberg.

Good stuff.

Alright, I have a couple of pro tips for running Prettier.

I'll have pro tips for each of these three tool types.

The first thing, I highly recommend if you use an editor such as VS Code, install the default, most common Prettier extension.

That's, I don't remember what ES Ben P stands for, but that's, I think, the person who wrote the Prettier extension for VS Code.

And then also, you can set your editor to use Prettier as a default formatter rather than the built in JavaScript formatter, which is fun fact, powered by TypeScript and VS Code.

And, you can do editor.format.save.

Who here uses editor.format.save?

Most of the room?

Who here relies on editor.format.save?

Yeah, it's so nice.

You save and you know whether you screwed up your syntax based on whether it all gets rewritten.

It's a really good example of small psychological manipulation actually being really good and useful in dev tooling.

There are many examples of those.

And this is of course in the template repo.

And by the way, this, is actually not that controversial anymore, because, tabs, accessibility, important, good.

But this is generally the only customization I do in, repos for, off the prettier default settings.

I add in a few plugins now for sorting stuff in my package JSON, but this is it.

We're clean.

We're good.

We're happy.

This wasn't, too earth shattering.

We're not, finding bugs.

We're just formatting our code, which is nice, because you go onto a new code base.

You don't have to adjust to someone else's style.

You just write in it formats.

You don't have to choose which style when the file is written by two authors, and the first half is tabs, and the second half is semicolons with spaces, or whatever.

Let's talk about linters now.

This is the stuff I really care about.

And no, we did not just cover this.

A linter is not a formatter.

More on that later.

A linter is a tool that runs a set of discrete checks on your code.

A set of checks on your source code.

Each of those checks is called a rule.

Anyone here heard of a linter rule before?

Curious?

Yeah, a lot of you.

Thank you.

You've all had to disable them with comments.

Yeah.

Each of those rules may report on a specific type of complaint or issue in your source code.

Code it doesn't like.

And for each of those complaints, it may provide an autofix or set of suggestions for how to fix your code.

And this is just an example.

If we look at the friend equals friend or me example from before, there is a built in, so comes with ESLint, rule called logical assignment operators that would say, Aha!

Josh, you moron, you buffoon!

That's my internal monologue, not yours, I hope.

Assignment equals can be replaced with, fun fact, JavaScript has this new thing, or equals!

Anyone know about this?

It's cool.

Which actually brings up a really good application of ESLint.

These rules can inform, can document new, good, better practices.

This is objectively, I think better code.

It's simpler, easier to read, and that's, definitely not in my internal monologue, but I hope it's in yours.

If you want to get started, very similar to before, you install the thing, ESLint, because it tends to be much more configurable.

You can have a lot of rules.

Has its own ESLint slash config runner that will walk you through setting up a new config, which is really nice.

And then npx eslint dot will run eslint on dot, the current directory.

Good stuff.

Now, a couple pro tips.

Because a linter is a set of rules, you'll need to configure your rules.

And by default, the ESLint project provides a really good set of rules.

Module.

Exports.

Equals extends ESLintRecommended, enables all the recommended built in core ESLint rules.

However, you can configure them on your own.

A lot of projects do this, such as disabling rules that don't make sense for them, which is a good idea if a rule does not make sense for your specific project for you as a person, a developer, whatever.

Disable it.

That's fine.

You can also add options to rules sometimes.

And if you're using a framework, who here writes in a framework?

Everyone should.

You can, enable specifically, the plug in React one.

And because React hooks are still new and fancy in the grand scheme of things, there's a separate plug in for React hooks.

You can also configure ESLint parsing.

If you're using something like React or Preact or Solid, tell it that JSX is allowed.

And then, you can load in plugins and use settings for them.

But okay, this is too much.

I don't like having to show a config file that takes up the whole vertical.

So we're gonna, again, it's in the template, don't worry about it.

But another couple of cool flags for ESLint.

If you want to disable a rule on a specific line, you can put a slash ESLint disable next line.

But if that comment doesn't do anything, ESLint has a flag to detect.

It will report unused disabled directives, which is nice, because who wants extra comments, especially comments about ESLint, floating around in their, code base.

So if you have ESLint disabled-next-line no-console, fine.

But if you had switched it to my fancy logger, it would yell at you.

And also, ESLint, like Prettier, has an autofixer.

Again, not every rule provides a fixer necessarily, they don't have to, but a lot of the good ones do.

And then lastly, ESLint rules can be configured as off, warn, or error.

Warn shows up as yellow in your terminal, and yellow squigglies, and will not fail the build, cause an exit code 1, if they exist.

Errors show up as red squigglies, red text, and will fail the build, cause exit code 1.

The distinction is meaningless.

If you have a bunch of errors existing in your project, they just will exist over time.

Trust me, I've seen it happen.

Codebases with hundreds of yellow warnings.

So I highly recommend ESLint Max Warning 0.

Anyway, similar to Prettier, again, there is a really good built in ESLint, sorry, a highly recommended ESLint extension of VS Code.

I recommend that, similar to Prettier, you fix all auto fixable ESLint complaints on save of files.

So in addition to format on save, you also source.fixall.eslint as an action on save.

You don't have to know the distinction between an action and a format, just use the template.

So cool, you're good, we're happy.

Are we happy?

Yay!

Yes.

Cool.

Thanks.

We're happily cleaning.

But this is not actually my favorite area.

My favorite area is complaining about ESLint for formatting.

ESLint is not a formatter!

I decided to come back to this.

Stop using ESLint for formatting!

I'm so passionate about it.

The industry needs, I want you all to repeat after me.

Okay?

Three, two, one.

Stop using ESLint for formatting!

Thank you.

Emotionally cathartic.

There's an indent rule that comes with the Islam.

Don't!

Semi, max-len, don't do it.

Because these are not the same things.

A formatter reformats all in one pass.

Oftentimes, nowadays, they just are real fast.

But they don't find or fix bugs, they just do the formatting.

Whereas a linter is a multi pass process.

It runs up to 11 times if you have fix enabled.

It runs a set of discrete rules, each of which might modify your AST or code, which means it'd have to run again to see if there are new introduced failures and so on and so forth.

Which makes it slower, and has to have explicit logic for edge cases.

So a rule that individually targets whether you, say, indent properly is an extremely difficult thing to write, because it has to explicitly handle all the possible cases.

And if you've ever seen, TypeScript conditional types, or, multi nested Ramda ternaries, or whatever, it's a nightmare to figure out what people actually wanted to indent.

So please, as a linter maintainer, I hate this.

People ask, people file so many bugs on us for the formatting rules.

Stop using them.

Eventually they will go away.

Any complaints?

Any tomatoes?

Okay, we're good.

But my, okay, my actual favorite is TypeScript.

Love TypeScript.

TypeScript is awesome.

Everyone seems to love it.

The hype at this point is not whether you should use TypeScript, it's how you should use TypeScript.

TypeScript is a superset of JavaScript.

If you already know this, let us be a talk on how to explain TypeScript.

If you don't.

Welcome.

But the words superset of JavaScript don't mean a lot to people who don't have a mathematical basis or union set theory or whatever, so we oftentimes say that it is JavaScript with types, which, if you don't know what types are, might also be meaningless to you.

So instead, I'm going to describe to you functionally what it is.

JavaScript describes only your code's values.

You can declare a variable, let myValue, or const if you really care.

What is this?

We don't know.

It's not in the code.

It might be in a comment.

Who knows?

Whereas TypeScript lets you additionally define the value's intent.

MyValue is intended to only ever be a number.

Intent is not the same as values.

A separate concept.

So the word TypeScript actually can refer to four things.

TypeScript can refer to the programming language, which is not a tool, it's the idea, the concept of the JavaScript syntax, what you're allowed to write, plus new types specific stuff, which is awesome, love the types.

It can also refer to the program.

The .js files that are shipped in the NPM package by the TypeScript team, shout out TypeScript team.

It's a program that reads in your source files, JS TSS of 50 other extensions, and then lets you know if there's some complaints.

Something it doesn't like.

It could also refer to a compiler.

TypeScript compiler is a tool that, in addition to type checking, can also transpile from TypeScript to JavaScript syntax.

And it can refer to the language service, which is the tooling in your editor, such as VS Code or Vim or Emacs.

I always have to say both so I don't get beaten, that will run all these tools, the compiler, the type checker, and so on.

Four things.

And I just want to be very clear on this.

Because there's a hullabaloo on Twitter right now where Svelte is not using TypeScript type syntax and they switched to JSDoc and two articles said they're dropping TypeScript.

Now everyone thinks Svelte is dropping TypeScript.

Now, Svelte is, this is like such an edge case, Svelte is specifically using a different way of writing syntax, which is actually really cool.

TypeScript allows you to define your type syntax purely in JavaScript comments, so you're not using the type syntax, you're just using the type checker.

Cool stuff.

Anyway, this is why TypeScript is good, because if we just look at this myButton.js, we have no way of knowing whether this is safe or used safely.

If we, say, have this break the internet component, and fun fact, if you're not a front end developer and you're looking at this JSX syntax, that reinforces the point that we need tooling if you don't understand the code.

We don't know whether this break the internet component, just by looking at it, if it's good or not.

Especially if my button is in some npm package or something somewhere.

We don't know if it's okay to not provide an onclick, if it's okay to provide an onclick as a string or not.

In React, no, and some other thing, who knows?

Maybe we'll stumble onto correctly using onClick?

I don't know, maybe we'll provide onTap and get real annoyed when the button stops working on prod and our users are losing trillions of crypto or whatever.

So TypeScript, out of the box, is really useful for us.

TypeScript will look at this, and it could immediately detect, okay, my button says onclick in its destructured prop, and you're not providing one, so first complaint, property onclick is missing in type, you just provided nothing, but required to type onclick.

Second, it could look at this and say, hey, you said it's onclick, but here you said ontap.

Wrong!

Thank you, TypeScript.

That was my Conan O'Brien impression, if anyone is a fan of Team Coco.

So also, I should note, TypeScript, if you've ever dug into its config file, which is tsconfig.json, does need a little hint to let it know that you're writing JSX in a particular way.

And then TypeScript also has this awesome compiler option, noImplicitAny, which says, if you don't give me a way to understand what type something is, I'm gonna yell at you.

Implicitly it has the, any type, the, any type in TypeScript is the, this could be anything YOLO.

No, that's, actually really bad.

It's, better to use TypeScript with anys than not TypeScript at all.

But an any is like one of the few escape patches in TypeScript.

You're telling TypeScript, I don't know what it's gonna be, so let me do whatever I want.

Let me do whatever I want is never what you want to hear in any sort of programming language design.

No implicit any yells at you when you declare something in a way that TypeScript can't infer the type of.

It'll tell you, hey, make like an interface or something, or whatever three different common ways there are.

So I know, okay, onClick is a function that takes in no parameters and returns void and nothing, so that's not allowed to be a string.

TypeScript!

Woo!

Yay!

Thanks.

Alright.

Similar to before, TypeScript has an install, save it as a dev dependency, you can npxtsc init to create a new tsconfig for you, TypeScript config, and then just run it on the command line.

It'll complain about your type checking issues.

And transpile code.

This is a very common config that a lot of people start with when they're writing something, like React or Solid.

JSXPreserve says TypeScript, don't worry about who's providing my JSX elements.

If you're not a front end dev, don't worry about it.

Module tells TypeScript that I'm working in ES Next because it's 2023.

God only knows what module you're actually working in.

Strict enables a bunch of really good, rules, such as no implicit any.

We, like strict.

And then Target says, I expect my language, Target, my, my running environments to have JavaScript features up through, whatever it is, ES 2021.

Anyone here still support Internet Explorer?

One person, I couldn't tell if you were serious raising the hand.

Back in my day.

So there are a few other options.

Some people do end up enabling, myself included.

Esmoduleinterop lets the type system be a little more lenient if you're Importing commonJS into ESM or vice versa.

Let's not go into that.

There were three talks on that yesterday.

Force consistent casing and file names does what it sounds like.

If you mess up your casing, TypeScript will let you know.

Skip lib check tells TypeScript not to make sure that all your node modules have correct types, which is performance optimization and can be nice if you have conflicting versions of things.

But anyway, this is all, again, too granular.

It's taking up the full vertical.

Especially with TS configs, most of the time you should just let the framework do it for you.

Because massaging your module targets is, and stuff like that, can be a real pain in the butt, and that's probably not why you wanted to go into programming.

So as long as they're setting strict to true, so you're getting the full juiciness of the TypeScript type checker, YOLO it.

If you do have interest in all these type checking compiler options, aka.ms/tsconfig, for those who are not Microsofties, this is like the Microsoft bitly.

I always thought it was cute.

This documents all the tsconfig options.

It has explanations for all of them.

And another not the same, a linter is not the same as a type checker.

They are more similar than a formatter but they're not the same thing.

A linter runs a set of discreetly configurable rules.

No two rules should know whether the other is enabled.

Which means that it can run faster.

Some people implement their linters in some kind of parallelized way.

But a linter only sees one file at a time, traditionally.

It just looks at the AST, the source code of each file at a time for the rules.

Whereas a type checker is a more grandiose operation.

More of this gesture instead of this gesture.

It fully understands the types of your source code.

It looks at all your files and understands them together.

Which makes it smarter.

It can see things across file boundaries like an implicit any, but it's slower, because it has to understand all those things at once.

God dang it, this clicker.

A couple pro tips, I, yeah, so I've mentioned that the ESLint rules can be worn or error.

I have always found it confusing that ESLint complaints show up as red and TypeScript complaints show up as red.

And a lot of people I've talked to don't understand the difference between ESLint and TypeScript a result.

A red squiggly is a red squiggly to many of us.

So I recommend set your ESLint rule customizations to the warn, the yellow squiggly.

So that all your ESLint complaints are yellow.

I actually added this to VS Code's extension.

I'm very proud of it.

And then also, make sure that your projects are using the local Node modules version of TypeScript because if you have one installed globally, it might not be the same version and then you have different complaints in your terminal versus your editor.

Yet again, I will remind you, probably don't set this up yourself.

Just use my template or another good one and you're good to go.

Just copy and paste this from the slides and forget about me.

Lastly, the last major area of tooling is the stuff that I personally work on, TypeScript ESLint because, ESLint does not natively support TypeScript.

TypeScript is not JavaScript.

It is a superset.

So we provide two packages, among others, that are quite popular in the JavaScript community.

The first is we provide the parser that allows ESLint to read in TypeScript code, TypeScript ESLint parser.

So all those new types comments thing, the interface, whatever, that's something that's now supported in ESLint thanks to this package.

And we provide over a hundred Lint rules that are specific to TypeScript.

Everything from using the right way or more common way to declare your interfaces or arrays to detecting async promise violations.

Cool stuff.

In addition to whatever ESLint recommended rules, there's also a plugin TypeScript ESLint recommended if you use rparser and rplugin to provide those rules.

And just to be clear, a parser is a thing that lets ESLint parse code more smart.

A plugin is a package that provides a set of lint rules.

So parser is how it parses code.

Plugin provides rules.

The two are different.

And just to reinforce how fun linting is, fun fact, TypeScript has its own set of quirks and things you can do multiple ways.

In this object, we are using what's called an as assertion or type assertion to say that name is not just any string, it's specifically the string DropBears.

I'm surprised no one laughed at that, honestly.

Every time I go to Australia, some jerk is like, Hey, you're about to drop it.

Anyway, yeah, there's a lint rule that says, Hey, there's actually a simpler way to do this.

You can just say, as cost.

So smart, look at you.

Another example of ESLint teaching us something new and fun.

I wish that I could combine the rules.

I wish I could combine the power of TypeScript with the configurability of ESLint.

Because if we were to be able to do that, five minutes left, we would actually create a whole new category of things.

We would take the TypeScript type checkers and the linters and make a new category of types.

Yeah, that's what I do.

There's a new category of lint rules you can use in TypeScript ESLint.

They are type check lint rules.

They are lint rules that use the power of TypeScript to make deductions about your code.

For example, does anyone know what happens if you await a console.log or something that's not available?

A promise?

It just I think I heard someone mutter under the breath that just returns it.

Yes, it, the await is unnecessary.

You don't need it.

And because most of you, what, we have a literal to let you know when your code base has that unnecessarily.

Fun fact, a thenable is the, is anything in JavaScript with a dot then?

Slightly more precise than that, but roughly.

A promise is the most common instance of the thenable interface.

So you just think of it as promises.

And we had a whole debate.

Should we call us await promise, await thenable?

But anyway, the.

await thenable could actually fix your code for you to remove the unnecessary weights which could help speed your code up, make it less convoluted.

And here's another example of why that very trivial example is, of a great rule that can help you a lot.

Look at this giant amount of code.

The vertical, it's too much.

We would get the same complaint because actually, fun fact, onClick is a function that returns void, not a promise.

So the rule would let us know, that's off.

So maybe you should say that onClick actually returns a promise.

Or, what if your onClick isn't asynchronous?

What if the rule is actually showing that you don't need the onWait?

Which means that then you don't need to set running true and false because it's always going to be false because onClick is synchronous.

Which means that this whole function is unnecessary.

Which means your running state is unnecessary.

Which means your button disabled is always going to be unnecessary.

Which means we can get rid of half the code!

Thanks to the power of TypeScript, be a Slate!

Yay!

Woo!

Thank you.

You clapped half a second before the clap cue was very good.

Awesome.

Here's the annoying thing.

Typed rules are slower because they pull in the power of the type checker, which is a slower thing.

So we don't enable them by default.

We're working with the TypeScript team to try to get so that they're fast enough that people don't get annoyed.

In the meantime, there's a second set of recommended TypeScript ESLint rules, recommended recurring type checking, parser options dot project to tell our parser to refer to TypeScript with whatever TSConfig is closest to each file.

That's a relatively new feature, I also added this one, very proud of it.

Yay, love that.

And then you can also combine React, whatever.

So I have a full repo, love my templates, that goes over this stuff, 2023, shows that bug and more, good stuff.

So let's recap, I'm almost done, I promise.

We went over static analysis.

Fear!

Uncertainty!

Doubt!

No!

We went over formatting, linting, stopped using ESLint for formatting, and TypeScript, those three core traditional things, along with a fourth ESLint plus TypeScript.

Each of those has a website.

prettier.io, eslint.org, typescriptlang.org, typescriptesline.io.

All these websites are great and open source and fun.

Yay.

If you want to get started with this stuff, would recommend check your formatter config.

Are you using prettier?

Are you using ESLint?

What is, your config?

Check your linter config.

If you're using a TypeScript code base with types of BS link, do you have the recommended requiring type checking rules?

Check your editor settings like you auto fix and save.

And if you happen to maintain a large framework such as something used by company or like a really important thing in open source that's used by a lot of react projects, check to make sure that you're like preset configs that everyone is using.

Actually do enable the aforemention important esent rules because most people don't.

So this is funny and totally.

I had, this is a funny start, but yeah, so like I'm actually working, trying to get Remix and NextJS to pull in those rules, please upvote these discussions.

Remix has actually been responsive, not NextJS.

Okay, cool.

So, TypeScript BSLint is a fully independent open source project.

We don't receive funding from Microsoft, we receive funding from individuals and generous companies.

You can support us monetarily.

That'd be lovely.

You can also support us by contributing on GitHub, filing issues, sending pull requests, just letting me know things that are good or bad.

That would make me happy.

I'm also an independent open source maintainer.

I don't have a real job.

My spouse has health insurance.

Yay, America.

And I also wrote a book on TypeScript.

Learning TypeScript.

So if you want to work on open source or support me in making this stuff better for you, please let me know.

Lastly, thank you.

This has been an absolute pleasure and let me know if you have any questions.

Don't Fear

NEEDS MORE LINT RULES

JoshuaKGoldberg.com

Animated GIF of the more cowbell sketch from Saturday Night Live
The image contains no transcribable text apart from the main term "FUD" and a signature/branding "@ JoshuaKGoldberg.com". The term "FUD" does not form a logical structure for HTML, and the signature/branding is not a footer but rather a part of the image graphic. Since the request is to convert transcribed text into structured HTML and there is no substantial text to structure, I will provide the branding in a footer element as per the instruction.

FUD

Surrounding the letters are various yellow emoji faces expressing fear, anxiety, and sadness.

FEAR

Red editor squigglies!?
Something broken?!

UNCERTAINTY

I don’t know why it’s angry (and I haven’t read the docs)

DOUBT

I know my code works, let's just ignore the linter...

DON'T FUD IT UP

STATIC ANALYSIS

Static Analysis

why so squiggly?

Static Analysis

Tooling that scrutinizes code without running it.

(in contrast with "dynamic analysis": e.g., testing)

Common Forms of Static Analysis

Speed

  • 0. Formatters ✍
  • 1. Linters ️‍♂️
  • 2. Type Checkers
Power

The image depicts a horizontal spectrum to represent common forms of static analysis. On the left end, signifying 'Speed', there are 'Formatters' represented by a pencil icon. Moving right towards the center, denoting a balance between speed and power, there are 'Linters' depicted with a magnifying glass icon. Finally, on the right end, indicating 'Power', there are 'Type Checkers' symbolized by a brain icon. This conceptual spectrum conveys a progression from less powerful but faster tools to more powerful but potentially slower tools in static code analysis.
GIF of a person belly flopping into lacke or the ocean from a rock.

What’s a Formatter?

(insert tabs/spaces snark here)

Formatting

A formatter is a tool that reads in your source code, ignores your formatting, and rewrites it.

Example: Rewriting Code

friend = friend
    || "me"

Example: Rewriting Code

friend = friend || "me"

Example: Rewriting Code

The image shows a side-by-side comparison of a piece of JavaScript code and its abstract syntax tree (AST) representation. On the left, the code assigns a value to a variable 'friend' using a logical OR operator with a string literal "me". On the right, a structured JSON-like object describes the AST of this code snippet, including the expression type, operators, and operands involved.

Abstract Syntax Tree

"AST" is what we call the format static analysis tools read your source code into.

Running Prettier

npm install prettier --save-dev
npx prettier . --write
  

GitHub Repository

github.com/JoshuaKGoldberg/template-typescript-node-package

Prettier VS Code Settings (Optional)

// .vscode/extensions.json
  {
    "recommendations": ["esbenp.prettier-vscode"]
  }

// .vscode/settings.json

{
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.formatOnSave": true
  }

Configuring Prettier

// .prettierrc.json
{
  "useTabs": true
}
  
An otter floating on its back in water, with its paws up near its chest.

What's a Linter?

...didn't we just cover this?

Linting

A linter is a tool that runs a set of checks on your source code.

Lint Rules

Each of those checks is called a "rule".

Each rule may report on code it doesn’t like, and each complaint may contain an optional autofix.

Example: Teaching New Syntax

{
  "rules": {
    "logical-assignment-operators": "error"
  }
}
friend = friend || "me";

Example: Teaching New Syntax

{
  "rules": {
    "logical-assignment-operators": "error"
  }
}
  

friend ||= "me";

so smart look at you

Running ESLint

npm install eslint --save-dev
npm init @eslint/config
npx eslint .
  

Configuring ESLint

// .eslintrc.js
module.exports = {
  extends: ["eslint:recommended"]
};
  

Configuring ESLint Rules

// .eslintrc.js
module.exports = {
  extends: ["eslint:recommended"],
  rules: {
    "logical-assignment-operators": ["error", "always"],
  },
};

Configuring ESLint for React

// .eslintrc.js
{
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended"
  ],
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    }
  },
  "plugins": ["react", "react-hooks"],
  "settings": {
    "react": {
      "version": "detect"
    }
  }
}

Running ESLint, Even Better (Optional)

// package.json
{
  "scripts": {
    "lint": "eslint . --report-unused-disable-directives"
  }
}

Running ESLint, Even Better (Optional)

// package.json
{
  "scripts": {
    "lint": "eslint . --report-unused-disable-directives"
  }
}
// eslint-disable-next-line no-console
console.log("hi");

ESLint VS Code Settings (Optional)

// .vscode/extensions.json
{
  "recommendations": ["dbaeumer.vscode-eslint"]
}

// .vscode/settings.json
{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  "editor.formatOnSave": true
}
  
A still from a scene showing an indoor setting with a person vacuum cleaning the floor. The person is walking to the right, pushing a red vacuum cleaner.

STOP USING ESLINT FOR FORMATTING

NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO

{
    "rules": {
        "indent": "error"
    }
}
  

They're Not The Same!

Formatter

  • Reformats in one pass
  • Faster
  • Can't find or fix bugs

Linter

  • Runs a set of discrete rules
  • Slower
  • Explicit logic for edge cases

Wasting time supporting formatting rules in ESLint

The image contains a still from a television show depicting a character looking frustrated or bored while sitting at a desk with a computer.

What's TypeScript?

Why won't tech Twitter shut up about it?

TypeScript is a “superset of JavaScript”.

(a.k.a. “JavaScript with types”)

JavaScript describes only your code’s values.

let myValue; // what is this?!

TypeScript describes your values’ intent.

let myValue: number; // oh ok

TypeScript is Four Things

Language Service

Program that runs the type checker inside an editor (IDE) (e.g., VS Code).

Compiler

Program that runs the type checker and outputs equivalent JavaScript.

Type Checker

Program that reads in files and reports on likely bugs / mismatches.

Programming Language

Describes the existing JavaScript syntax, plus new type-specific syntax.

Tweet from Josh. Tet reads:

Josh Goldberg

Using JSDoc to describe your types is not necessarily "dropping TypeScript"! It's only swapping what syntax you're using to write types.

You can type check JS files that use JSDoc, or skip type checking on TS files.

Without Type Checking

// MyButton.js
function MyButton({ onClick }) {
  return (
    <button onClick={onClick}>
      click me  
    </button>
  );
}

Without Type Checking

// MyButton.js
function MyButton({ onClick }) {
  return (
    <button onClick={onClick}>
      click me  
    </button>
  );
}

function BreakTheInternet() {
  return (
    <>
      <MyButton />
      <MyButton onClick="wat!" />
      <MyButton onClick={() => alert("clicked  ")}/>
      <MyButton onTap={() => alert("WRONG  ")}/>
    </>
  );
}
  

Without Type Checking

// MyButton.js
function MyButton({ onClick }) {
  return (
    <button onClick={onClick}>
      click me  
    </button>
  );
}

function BreakTheInternet() {
  return (
    <>
      <MyButton />
      <MyButton onClick="wait!" />
      <MyButton onClick={() => alert("clicked  !")} />
      <MyButton onTap={() => alert("WRONG  !")} />
    </>
  );
}
  

Type Checking React

code examples as in the previous slide, but with these type errors called out


            <MyButton/> // Property 'onClick' is missing in type '{}' but required in type '{ onClick: any; }'.
             <MyButton onTap={() => alert("WRONG  ")}/> // Type '{ onTap: () => void; }' is not assignable to type '{ onClick: any; }'.
        </>

    

Code from previous slide with the following added

// tsconfig.json
  {
  "compilerOptions": {
    "jsx": "preserve"
  }
}

Type Checking React (v2)

// MyButton.tsx

function MyButton({ onClick }) {
  return (
    <button onClick={onClick}>
      click me  
    </button>
  );
}
  

// Binding element 'onClick' implicitly has an 'any' type.

function BreakTheInternet() {

return (
  <>
    <MyButton /> // Property 'onClick' is missing in type '{}' but required in type '{ onClick: any; }'.
    <MyButton onClick="wait" />
    <MyButton onClick={() => alert("clicked  ")} />
    <MyButton onTap={() => alert("WRONG  ")} /> // Type '{ onTap: () => void; }' is not assignable to type '{ onClick: any; }'.
  </>
);
}

// tsconfig.json

{
  "compilerOptions": {
    "jsx": "preserve",
    "noImplicitAny": true}
        

Type Checking React (v2)

// MyButton.tsx

interface MyButtonProps {
  onClick: () => void;
}

function MyButton({ onClick }: MyButtonProps) {
  return (
    <button onClick={onClick} type="button">
      click me  
    </button>
  );
}

function BreakTheInternet() {
  return (
    <>
      <MyButton /> // Property 'onClick' is missing in type '{}' but required in type '{ onClick: () => void; }'.
      <MyButton onClick="Wait" /> // Type 'string' is not assignable to type '() => void'.
      <MyButton onClick={() => alert("clicked  ")} />
      <MyButton onTap={() => alert("WRONG  ")} /> // Type '{ onTap: () => void; }' is not assignable to type '{ onClick: () => void; }'.
    </>
  );
}
    

A Starter TSConfig for React

// tsconfig.json
{
  "compilerOptions": {
    "jsx": "preserve",
    "module": "ESNext",
    "strict": true,
    "target": "ES2021"
  },
}

A Nicer TSConfig for React

// tsconfig.json
{
  "compilerOptions": {
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "jsx": "preserve",
    "module": "ESNext",
    "skipLibCheck": true,
    "strict": true,
    "target": "ES2021"
  },
}

Frameworks usually write a TSConfig for you.

Just make sure they’re setting strict: true!

aka.ms/tsconfig

They’re Not The Same!

Traditional Linter

  • Discretely configurable rules
  • Faster
  • Only sees one file at a time

Type Checker

  • Fully understands types
  • Smarter
  • Slower

TypeScript VS Code Settings (Optional)

// .vscode/settings.json
{
  "eslint.rules.customizations": [
    { "rule": "*", "severity": "warn" }
  ],
  "typescript.tsdk": "node_modules/typescript/lib"
}

TypeScript ❤️ ESLint

A match made in static analysis heaven

@typescript-eslint/parser

tells ESLint how to read TypeScript syntax

@typescript-eslint/eslint-plugin

provides ESLint rules for TypeScript

Configuring ESLint for TypeScript

// .eslintrc.js
module.exports = {
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended"
  ],
  parser: "@typescript-eslint/parser",
  plugins: ["@typescript-eslint"],
};

Example: Teaching TypeScript Syntax

{
  "rules": {
    "@typescript-eslint/prefer-as-const": "error"
  }
}

let me = { name: "Drop Bears" as "Drop Bears" };

Example: Teaching TypeScript Syntax

{
    "rules": {
      "@typescript-eslint/prefer-as-const": "error"
    }
  }
  

Expected a const instead of a literal type assertion.

Common Forms of Static Analysis

Speed

  • 0. Formatters ✅
  • 1. Linters (Traditional)

Power

  • 2. Type Checkers
  • 3. Linters (Type Checked)

Slide depicts a spectrum from "Speed" on the left to "Power" on the right, indicating a trade-off between the two in static code analysis tools.

On the left side, under "Speed," the first item is:

0. Formatters (depicted with a checkmark emoji)

In the middle, representing a balance between speed and power, is:

1. Linters (Traditional) (shown with a magnifying glass emoji) On the right side, under "Power," the list continues with:

2. Type Checkers (represented by a brain emoji)

3. Linters (Type Checked) (indicated by a flame emoji)

Each item on the list represents a type of static analysis tool, ordered by their relative execution speed and analytical power. Formatters are the fastest but least powerful in terms of analysis, traditional linters offer a middle ground, and type checkers along with type-checked linters provide the most powerful analysis but are presumably slower.

Example: Detecting Incorrect Async Code

{
  "rules": {
    "@typescript-eslint/await-thenable": "error"
  }
}
await console.log("wat");

Example: Detecting Incorrect Async Code

{
  "rules": {
    "@typescript-eslint/await-thenable": "error"
  }
}
    
interface MyButtonProps {
  onClick: () => void;
}

function MyButton({ onClick }: MyButtonProps) {
  const [running, setRunning] = useState(false);

  async function onClickAndDisable() {
    setRunning(true);
    await onClick();
    setRunning(false);
  }

  return (
    <button
      disabled={running}
      onClick={onClickAndDisable}
      type="button"
    >
      click me  
    </button>
  );
}
    

Example: Detecting Incorrect Async Code

interface MyButtonProps {
    onClick: () => void;
}

function MyButton({ onClick }: MyButtonProps) {
    const [running, setRunning] = useState(false);

    async function onClickAndDisable() {
        setRunning(true);
        await onClick();
        setRunning(false);
    }

    return (
        <button
            disabled={running}
            onClick={onClickAndDisable}
            type="button"
        >
            click me  
        </button>
    );
}

Correct Async Code

interface MyButtonProps {
    onClick: () => void;
}

function MyButton({ onClick }: MyButtonProps) {
    return (
        <button
            onClick={onClick}
            type="button"
        >
            click me  
        </button>
    );
}

Really Configuring ESLint for TypeScript

// .eslintrc.js
module.exports = {
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:@typescript-eslint/recommended-requiring-type-checking",
  ],
  parser: "@typescript-eslint/parser",
  parserOptions: {
    project: true,
  },
  plugins: ["@typescript-eslint"],
  root: true,
};

Really Configuring ESLint for React and TypeScript

// .eslintrc.js
module.exports = {
  extends: [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:@typescript-eslint/recommended-requiring-type-checking",
  ],
  parser: "@typescript-eslint/parser",
  plugins: ["@typescript-eslint", "react", "react-hooks"],
  root: true,
  settings: {
    react: {
      version: "detect",
    },
  },
};

github.com/JoshuaKGoldberg/linting-typescript-in-2023

Recap

  1. Static Analysis FUD
  2. Formatting
  3. Linting
    • STOP USING ESLINT FOR FORMATTING
  4. TypeScript
  5. ESLint + TypeScript

Resources

Next Steps

  1. Check your formatter’s configuration
  2. Check your linter’s configuration
  3. Check your editor’s settings
  4. If you maintain a large framework (such as a full-stack React.js app framework and builder), make sure its recommended ESLint config extends the aforementioned recommended configs

Support Us

typescript-eslint.io

open-collective.com/typescript-eslint

About Me

Open Source Maintainer

I focus on static analysis tooling around JavaScript & TypeScript.

Author, Learning TypeScript (O’Reilly).

Microsoft MVP

Support Me

Learning TypeScript
Enhance Your Web Development Skills
Using Type-Safe JavaScript

Josh Goldberg