Tim Deschryver

A new bit every Tuesday of a tool || feature || blog that has helped me, or which I encountered recently and impressed me.

20. Use jq to query and manipulate JSON data

jq is a lightweight command-line tool to quickly process your JSON data. I mainly use it to search (grep) and select specific fields of a JSON file, but it can also be used to transform (sed) and process (awk) the data. Of course, the output is also nicely formatted.

If this peaks your interest head over to the download page.

While these examples are simple and are using a local data source, you can feed data to jq using the pipe (|) operator.

Make sure to check out the tutorial and the playground to also learn how to fully take advantage of jq by transforming and filtering JSON data.

19. NuGet Central Package Management

When you've worked in a mono-repository that contains multiple C# projects, you've probably felt the pain of maintaining NuGet dependencies. Updating packages to a newer version, keeping versions in sync across different projects, removing outdated dependencies, ...

With Central Package Management (CPM) this daunting task becomes easier to manage. By creating a file Directory.Packages.props within the root of the project, all NuGet package references can be defined with their corresponding version.

Next, the version of the package references that are defined within the project (.csproj) files can be removed.

The result is a file that contains the single source of truth when it comes to your package versions.

You can still override versions for one-off cases, or subdivide this file into multiple files. For more info about CPM, see the documentation.

18. Pretty TypeScript types

I learned this trick from Matt Pocock, and I've seen the power by seeing Marko Stanimirović using it in the NgRx codebase. Because I think that we'll see more usage of this, I wanted to make a bit out of it.

You've probably already encountered TypeScript types where you scratch your head and think "What does this even mean?". From my own experience, this can happen when you interact with complex types from 3rd party packages.

Using the following Prettify utility type, which you need to create yourself, some of the types will become more readable.

Prettify takes a generic parameter T and maps over its keys using a mapped type to essentially create a new type that has the same properties as T.

🤖 Here's a breakdown of what's happening:

  • type Prettify<T>: This is a declaration of a new type called Prettify that takes a generic parameter T. T is a placeholder for any type that you want to use with Prettify.
  • [K in keyof T]: This is a mapped type. It iterates over all keys of T (represented by K). keyof T is a type that represents all keys of T.
  • T[K]: This is an indexed access type. It represents the type of property K in T.
  • & {}: This is an intersection type. It combines multiple types into one. In this case, it's intersecting the mapped type with an empty object type. However, since an intersection with an empty object doesn't change the type, this part doesn't have any effect on the Prettify type.

The unwrapping of object properties turns resolved types that were previously difficult to comprehend, into types that become easier to read and understand.

Let's take a look at an example to make this clear:

With a small modification, it can also unwrap child properties.

For more info see Matt's blog The Prettify Helper.

17. Edit and send HTTP requests directly from the Browser DevTools

Did you know you can use the browser DevTools (in Microsoft Edge and Firefox) to edit and send network requests?

This little trick can save you some time. Instead of manually reproducing a request, or copying the request via the network tab, you can right-click a network request to edit the URL, headers, body, etc, and resend the edited request! 🚀

16. How are the TypeScript Partial and Required types implemented?

I find it important to know how the most-used TypeScript utility types are implemented. Here, we see the implementation of the Partial (all properties of a type are optional) and Required (all properties of a type are required) types. Knowing this is essential because it allows you to customize or create a new behavior for your use cases.

15. Bulk updates in Entity Framework

Use bulk updates to update entities with a single command (without the need of the change tracker).

The entities will not be loaded into memory, which results in more efficient updates and deletes.

For more info, see my blog post "New in Entity Framework 7: Bulk Operations with ExecuteDelete and ExecuteUpdate", or the documentation.

14. How to quickly open a GitHub repository within an IDE

As developers, we read more code than we write.

We don't only read our own code that we wrote ourselves, or the code from our project, but we also come in contact with code from 3rd parties.

Personally, I like to browse through code to learn or to investigate a behavior or problem that I encountered. While doing so it's useful to be in a comfort zone where you already know how to navigate within the code base to find the thing that you're looking for.

The GitHub interface has improved in the last couple of years, but it isn't the same as using an IDE. An IDE has the advantage that it's smarter. It can, for example, show all the references, go to the implementation with a simple click, and more.

To have that familiar IDE experience without having to clone or download the repository locally, which can be time-consuming especially if you don't have all the dependencies installed, there are a few options.

Here are the ones that I like to use, to quickly open a codebase within a web-editor to have that familiar IDE experience. As a side note: all of them are powered by Visual Studio Code.

13. New in C# 12: Collection expressions

Creating collections becomes simpler in C# 12 using collection expressions. We can also make use of the spread operator .. to copy the values from a collection.

More info can be found in the documentation.

12. Polyglot Notebooks

As developers, we sometimes need an isolated environment or a testing sandbox. For example, to reproduce a problem, or to share a snippet with a team member.

This is already possible using online REPLs e.g. TypeScript playground and .NET fiddle, or we can resort to web containers for example GitHub Codespaces or Stackblitz.

The problem that I encounter with these tools is that I often forget or lose previously created proof of concepts, or that it's hard to share them with my team.

That's not the case with Polyglot Notebooks.

Using notebooks has the following benefits:

Some use cases where notebooks can be useful:

Get the Polyglot Notebooks Visual Studio Code extension in the marketplace.

11. Angular Control Flow

The past week I cursed Angular a little bit because I just wanted to have a simple if-else condition within my HTML template. But, as you probably already know, we have to jump through a couple of hoops to get this working. The result is a not-so-easy-to-read template that's prone to errors.

Angular 17 changes that by providing a new (opt-in) way on how we write our templates. Instead of using the structural directives NgIf, NgFor, and NgSwitch, you'll be able to use the new @ syntax.

This change drastically improves the Developer's Experience;

As an example, let's take a look at my if-else condition and compare both solutions.

For completeness let's also take a look at the new syntaxes to iterate over a collection (*ngFor) and how to create a switch expression (*ngSwitch).

To already try out the new Control Flow feature before it's officialy released, update the angular compiler options within the TypeScript configuration file. Add the _enabledBlockTypes property, and add the blocks you want to use within your application.

For more information see the Angular blog post Meet Angular’s New Control Flow.

10. Cypress to Playwright converters

Migrating from one test library to another can be a daunting task, but with the right tools, we can make it a smoother transition.

To help with this task I've encountered two similar tools that rewrite your Cypress code to its equivalent in Playwright.

The first of these tools is https://demo.playwright.dev/cy2pw/, and the second one https://ray.run/tools/cypress-to-playwright (you should also check out the blog on this website for more goodies!).

On the websites, you can simply paste your Cypress code, and you'll receive the test cases rewritten in Playwright ✨. Just like that.

These tools can be used for a couple of reasons:

In short, the mentioned tools help you with the transition from Cypress to Playwright.

Side note: if you have a GitHub Copilot license you can also prompt your Copilot to migrate a test or file for you. In a small demo application, this worked out fine for me.

Here's a small example to give you an idea of a migration:

For more Playwright content, check out my blog posts about 🎭 Playwright.

9. Switch Exhaustiveness

How many times have you added a new option (enum, union, expression, ...) without updating all code paths? Did this result in unforeseen issues?

One of the reasons why I prefer switch statements over if-else conditions is that it supports exhaustiveness checks. This is incredibly useful because it ensures that no scenario is left unhandled, reducing the risk of unexpected bugs or runtime errors. It also shortens the feedback loop because your IDE immediately flags incomplete switch statements.

Personally, I also find a switch statement more readable in contrast to an if-else condition.

The Switch Exhaustiveness check is not enabled by default, so you'll have to enable it.

In C# applications the default behavior is that a non-exhaustive switch is a warning. To turn this into an error, configure the TreatWarningsAsErrors compiler option in your project(s). If you just want to treat specific warnings as an error, use the WarningAsErrors compiler option instead, which accepts warning numbers, e.g. <WarningsAsErrors>CS8509</WarningsAsErrors>.

Now, when a switch is not exhaustive, it results in a compilation error.

For TypeScript applications, enable the switch-exhaustiveness-check ESLint rule. It's best to configure this rule as an error so it catches your attention.

Enabling the ESLint rule doesn't prevent the application from building. As an alternative, manually add a default case that turns into a compiler error when it detects a non-exhaustive switch. For this, you can use the never type. Now this will also result in a compiler error.

8. The Evolution of C# Constructors

C# 12 introduces a new syntax to declare class constructors with Primary Constructors. That's why I thought that it's a good time to look back at the evolution of C# constructors. Starting from the most basic constructor that most of us are familiar with, to the newer variations that were added in latest the C# versions.

7. Simplified Node.js Version Management with fnm

Are you working on multiple projects that each require a different Node.js version, and are you tired of juggling between them? Then I've got the right tool for you, fnm (Fast Node Manager).

fnm is a Node.js version manager that allows you to install and switch between different Node.js versions on the fly.

What I especially like about fnm, compared to similar version managers, is that it provides a seamless experience by automatically switching to the correct version when it detects a .node-version (or .nvm) file in your project's directory.

For example, let's say you have two projects, awesome-project and great-project, and each of them requires a different Node.js version:

In the preceding example, fnm automatically switched to the correct Node.js version for each project. Because awesome-project requires a Node.js version that isn't installed yet, we received a prompt asking if we want to install it.

To set the Node.js version for a project, create a .node-version file in your project's directory and add the version number to it:

Other helpful fnm commands are install, use, default, and current:

Bonus: GitHub Codespaces is great to use for switching between multiple project configurations that require bigger changes, when you just need a clean environment to work in, or to quickly test something out. You can easily create a new Codespace while working (or using an existing Codespace for reviewing) for changes that have a big impact on your environment, e.g. an upgrade of an SDK, such as .NET, Node.js, ...

6. Git Log Heatmap

Use git log to create a heatmap of the files that have been changed the most frequently within a repository. This is useful to identify the files that most likely need a closer look. For example, the file contains too much logic and should be split up into multiple files, it contains hot code paths that change frequently, or maybe it's a file with a history of many fixes.

The following git log command returns a list of files (excluding json and lock files) that have been changed within the last 6 months, sorted by the number of changes.

5. Raw SQL Queries for Unmapped Types in Entity Framework 8

Entity Framework 8 has a new feature that allows you to execute raw SQL queries against the database and return results as unmapped types. To use this feature, use the new SqlQuery method on the Database property of your DbContext instance.

This feature is useful when you want your query to return a specific type for a specific purpose. For example, in many cases you don't need/want the overhead of returning your full-blown entity for search queries. Instead, you want a optimized entity (e.g. a DTO) that only contains the data you need for that specific purpose. Usually this results in a faster query and less data transferred over the wire.

See my blog post You can now return unmapped types from raw SQL select statements with Entity Framework 8 for more info about this new feature.

4. Big List of Naughty Strings

Does your application correctly handle all kinds of input correctly? Do you need some inspiration to test your application with some edge/special cases?

Look no further, the Big List of Naughty Strings is here to help you! There are different sets of inputs, from the boring "Reserved Strings" and "Special Characters" cases to the more interesting variants like "(Server and Client) Injections", "Unicode fonts", "Known CVEs and Vulnerabilities", "Special Filenames", and more.

Take a look at the repository and start copy-pasting some of the strings in your application, or use one of the libraries (e.g. NaughtyStrings for .NET, or blns for Node.js ) to automate this process.

Fun fact: I couldn't generate the banner for this bit (in various tools) because it contained a few naughty strings that broke the export.

For the entire set see big-list-of-naughty-strings/blns.txt, here's a small sample:

3. Http Files

.http files are a simple way to quickly invoke your API endpoints because you don't have to leave your IDE to use a separate tool. I use it regularly when I'm building a new feature.

You can commit this file to your repository so that your teammates can also make use of it!

HTTP files are supported by most IDEs (for Visual Studio Code, you'll have to install the REST Client extensions), the only caveat is that the variables syntax is (currently) different between Visual Studio products and JetBrains products.

Here's an example of a .http file for a Todo API:

For more information, check out the documentation for the various IDEs:

2. Angular Input enhancements

The Angular 16 release adds useful enhancements to the @Input decorator. We can make an input required, we can transform the input value, and we can bind inputs route data (parameters, query parameters, and the route data property).

We can transform the input value by providing a transform method, which is invoked with the input value. Angular already provides the numberAttribute and booleanAttribute transform methods, but you can also implement your own.

To be able to bind the route data to component inputs, you need to configure the Angular router by including the new withComponentInputBinding().

1. Tools to keep your NPM dependencies up-to-date

You can use the default npm commands npm outdated (check the registry to see if any packages are currently outdated) and npm update (update all the packages listed to the latest version, respecting the semver constraints of both your package and its dependencies).

But, to get a more detailed and prettier overview of the dependencies, you resort to the CLI tool 🥦 taze, which also works for monorepos.

The Visual Studio Code users among us can install the NPM extension to get a nice sidebar with the package information (and quick actions to update them). Just take a little glance at the sidebar to see if there are any updates available.

Follow me

You can find me on Twitter, LinkedIn, and GitHub.

Support me

I appreciate it if you would support me if have you enjoyed this post and found it useful, thank you in advance.

Buy Me a Coffee at ko-fi.com PayPal logo