TypeScript can be intimidating at first glance, especially when you're used to the relative simplicity of JavaScript (or allergic to the verbosity of other languages you might have studied, like me with Java). When I started learning it a few years ago, I found the code harder to scan, as if everything was slightly less readable.
I then searched for a way to better distinguish new code from existing code, and started exploring how VSCode handles visual themes.
Editor Tokens & Scopes
Similar to an Abstract Syntax Tree (AST), VSCode breaks down the code in each file into tokens to create an abstract view of it, making it easier to manipulate. This abstraction is then used to build themes and analyze the code.
To better understand this functionality, there are guides on syntax highlighting and semantic highlighting. The former focuses on static analysis, while the latter understands the code (for example, recognizing that a variable is actually a parameter and thus ensuring it retains the same color for consistency across occurrences):
Simply put, each character in your code is assigned:
- A token type: String, Comment, ... (complete list here)
- One or more scopes, which act like subsets or groups
Extensions can add new scopes, for example, for Vue files (Single File Components), which contain HTML, JavaScript, and CSS.
Customization
To help us understand this system, VSCode offers a command "Developer: Inspect Editor Tokens and Scopes" that displays a tooltip with information about the currently selected code. Here's an example with the compact
function from Lodash:
There’s a lot of information, but the only thing we care about for now is the list of scopes; the most specific scopes are listed first. We can use these to target the elements we want to customize.
Next, we move on to the VSCode settings to adjust the theme to our needs. The documentation explains that you need to edit your settings.json
file as follows (use the "Preferences: Open User Settings (JSON)" command):
{
"editor.tokenColorCustomizations": {
"textMateRules": [
{
"scope": [],
"settings": {}
}
]
},
}
As I navigated through the code over the weeks and experimented with rule specificity, I came up with this list of scopes (which is likely not exhaustive):
{
"editor.tokenColorCustomizations": {
"textMateRules": [
{
"scope": [
"support.type.builtin.ts",
"meta.interface.ts punctuation.definition.block.ts",
"meta.type.annotation.ts",
"meta.type.declaration.ts",
"entity.name.type.ts",
"support.type.primitive.ts",
"entity.name.type.alias.ts",
"entity.name.type.interface.ts",
"storage.type.interface.ts",
"keyword.operator.type.annotation.ts",
"punctuation.definition.typeparameters.begin.ts",
"punctuation.definition.typeparameters.end.ts",
"meta.object.type.ts",
"meta.type.parameters.ts",
"meta.object.member.ts meta.type.tuple.ts",
"keyword.control.satisfies.ts",
"keyword.control.as.ts",
"meta.type.declaration.ts punctuation.definition.block.ts",
"meta.type.declaration.ts keyword.operator.assignment.ts",
"meta.type.parameters.ts punctuation.separator.comma.ts",
"meta.type.parameters.ts punctuation.definition.block.ts",
"meta.type.parameters.ts meta.brace.square.ts",
"meta.type.annotation.ts keyword.operator.type.ts",
"meta.type.annotation.ts punctuation.terminator.statement.ts",
"meta.type.annotation.ts punctuation.definition.block.ts",
"meta.type.annotation.ts string.quoted.single.ts",
"meta.return.type.arrow.ts meta.type.tuple.ts",
"keyword.operator.definiteassignment.ts",
"keyword.operator.optional.ts",
"storage.type.type.ts",
"meta.interface.ts"
],
"settings": {
"fontStyle": "italic",
"foreground": "#ff0000"
}
}
]
},
}
As you can see, all scopes end with .ts, and scopes can be further refined to ensure they’re nested within another using a space (similar to a CSS selector): "meta.type.annotation.ts keyword.operator.type.ts".
The only thing left is to decide which styles to apply. Unfortunately, the options are limited: just text color and a few font styles (bold, italic, underline, strikethrough), or a combination of these. To avoid color conflicts with a theme, I chose to apply italics, which remain fairly discreet in daily use. I had considered using a very light background color, but this still isn’t possible in VSCode (open ticket since 2016).
Here’s the result on an example from the TypeScript documentation with "fontStyle": "italic":
Feel free to find what works best for you, for example, bold with "fontStyle": "bold":
You can even experiment with more unusual styles, such as filter: drop-shadow()
(it’s more complicated; here’s how the MoonLight theme does it)](https://github.com/robb0wen/synthwave-vscode/blob/ec7e97eba96febbcf069256a6513ecedd0b187ae/README.md)):
Conclusion
For me, this trick helped a lot in the beginning to better digest TypeScript's new concepts. I could remove this customization now, but I’ve grown used to it and kept it. I hope it helps some of you get started with TypeScript!
Top comments (0)