Josh Goldberg
Screenshot of a Chrome devtools performance profile

TypeScript Contribution Diary: Trailing Type Parameters

Feb 17, 201810 minute read

How I contributed a change to TypeScript that allowed trailing commas in type parameter/argument lists.

I’ve started making contributions to TypeScript, working my way up from small error reporting nitpicks to (hopefully!) real syntactic and control flow improvements. These blog posts are documentation of the steps I took to figure out what to do for these contributes & then do them. If you’re thinking of contributing to TypeScript or other large open source libraries, I hope seeing how to delve into a giant foreign monolith is of some help!

Next post: Pretty Error Counts

Problem Statement

TypeScript Issue 16152 (following Prettier Issue 1820): “trailing comma not allowed” error in type parameters. When you have a list of type parameters split across lines, some code styles and formatters would prefer you have a “trailing” comma after the last one to maintain consistency:

export const myLambda = <
	TFirstTemplate,
	TSecondTemplate // [ts] Error: Trailing comma not allowed.
>() => {
	/* ... */
};

That recently became a little more relevant because TypeScript added generic parameter defaults in addition to the existing extends clause. These giant syntax lists can be pretty hard to read unless split across lines:

export const createSourceNodeFromTemplateBinding = <
	TTemplateBinding extends TemplateBinding = TemplateBinding,
	TSourceNode extends SourceNode = SourceNode // comma here?
>(
	templateBinding: TTemplateBinding
) => {
	/* ... */
};

Something in TypeScript was complaining about those trailing commas.

Time to debug!

Step 1: Where

How does TypeScript create the complaint?

The complaint string was ”Trailing comma not allowed.”. I ran a full text search on that string to see where it comes from. There were a bunch of files, so I limited it to .ts files in the src directory. Exactly one result: src/compiler/diagnosticInformationMap.generated.ts. This file contains a giant key-value pair of lines with message-looking things. Spacing the one I found out for readability:

Trailing_comma_not_allowed: diag(
    1009,
    DiagnosticCategory.Error,
    "Trailing_comma_not_allowed_1009",
    "Trailing comma not allowed."),

Using the context clues from the “diagnostic” word in the file’s name and contents, this seems to be localized messages TypeScript can print out. 💯

Running a second full-text search on ”Trailing_comma_not_allowed” found exactly one method: checkGrammarForDisallowedTrailingComma, in src/compiler/checker.ts. From that name I was pretty sure this was what I needed. I also happened to remember reading in the (excellent) Basarat Gitbook how the ”Checker” in TypeScript is what validates types… and since this was in checker.ts, that felt good.

Step 2: What?

What does TypeScript do to create the error?

I ran a third full-text search on checkGrammarForDisallowedTrailingComma and found 5 usages in checker.ts. They were under these functions:

A diligent programmer intent on learning all there is to know about TypeScript might go read and understand each of these… but I could tell from the name that checkGrammarTypeParameterList was probably what I needed.

function checkGrammarForDisallowedTrailingComma(
	list: NodeArray<Node>
): boolean {
	if (list && list.hasTrailingComma) {
		const start = list.end - ",".length;
		const end = list.end;
		return grammarErrorAtPos(
			list[0],
			start,
			end - start,
			Diagnostics.Trailing_comma_not_allowed
		);
	}
}

Without knowing much about node arrays, list types, or any of the surrounding code, it looks like this was checking if the list hasTrailingComma and adding a grammar error if so. Exactly what I needed! ✔️

Step 3: Fix

I removed calling checkGrammarForDisallowedTrailingComma from checkGrammarTypeParameterList, rebuilt the compiler (here’s how to rebuild the compiler), and pointed my VS Code to my built TypeScript (here’s how to use a local TypeScript in VS Code). It worked! No IDE complaints for source files with trailing commas on type parameters!

At this point, I was spooked: surely there must have been much more to do. TypeScript is huge! There must be overcomplicated cruft everywhere.

Turns out I was wrong: that was basically it.

Step 4: Verify

TypeScript validates these checker behaviors with “baseline” tests. Each test consists of a source file of TypeScript (or JavaScript) code under tests/cases/** and corresponding expected details under baselines/reference/**. Manipulating them is documented in their CONTRIBUTING.md.

I compiled & ran tests to discover exactly one failure:

Test: tests/cases/compiler/typeParameterListWithTrailingComma1.ts

class C<T> {}

The test complaint was that there should have been an error in this, as per tests/baselines/reference/typeParameterListWithTrailingComma1.errors.txt. After my change there was no longer an error.

💖 Perfect! 💖

I marked the baseline as accepted and the rest of the tests passed.

Step 5: Pull Request

Allowed trailing commas in type parameter/argument lists by JoshuaKGoldberg · Pull Request #20599:

This change is only one source file and one error file… there must be something I’m missing!? Fixes #16152

At this point I was still pretty worried about the change being purely subtractive. DanielRosenwasser (a core TypeScript team member) and vjeux (the top committer to Prettier) responded to my post with the 😄 emoji and my fears completely melted away.

Bonus points to vjeux and novjek for the 🎉 emoji. If you maintain any size repository, let this be a lesson for you: emojis make first-time contributing much less scary. Even though this wasn’t my first TypeScript pull request, it was my first time changing the language itself (or anybody else’s language), and I was feeling the imposter syndrome hard. This might sound like a joke, but it isn’t: humanizing the process made it feel like it was geared for outsiders like me and not just dedicated full-time paid closed-source maintainers. Even the customary thanks from mhegazy (core TS team member who merged the PR) felt nice. 😄

I happened to send the pull request in the middle of December, so it wasn’t until early January that it was merged in… but it got in without any complaints.

Takeaways

  1. Find-in-Files is the only IDE command you need.
  2. TypeScript is just like any large code base: daunting if you let it, approachable if you try.
  3. Code maintainers with senses of humor are best. 🌹

Thanks for reading! I hope you got something out of this and to write more detailed posts on bigger contributions soon! 😅