Skip to content

Instantly share code, notes, and snippets.

@alexeagle
Last active January 17, 2019 11:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexeagle/58e079e91b8577eafb59323f95a0d617 to your computer and use it in GitHub Desktop.
Save alexeagle/58e079e91b8577eafb59323f95a0d617 to your computer and use it in GitHub Desktop.

Proposal: ts.DiagnosticPlugin and tswarn

The Angular team owns a couple of tools that would like to extend the diagnostics produced by the TypeScript compiler:

  • tsetse is our set of third-party --strict checks. So far, these are wired into our custom Bazel tsc compiler, tsc_wrapped, and they produce only Severity.ERROR diagnostics. See http://tsetse.info

  • Angular Ivy is a new rendering engine for Angular, and it allows a simpler compiler pipeline which we hope can fit entirely within tsc given a plugin model for producing extra diagnostics (such as semantic or type errors within Angular template expressions)

At the same time, TypeScript has an outstanding design issue with producing Severity.WARNING diagnostics (issue) and so far has decided not to emit these. However, some features such as --noUnusedLocals cause broken compilations where the program is not incorrect according to the type system or the TypeScript language specification, and it’s not clear why developers would want an unused variable to block their workflow.

As written in [CACM paper] we do not want to show existing warnings in the tsc output because developers have warning blindness and do not act on a newly introduced warning when it is obscured by a number of existing ones.

We also see a proliferation of linting tools, now that ajafff has forked https://github.com/fimbullinter/wotan/blob/master/README.md from TSlint. At the same time, TSLint itself went briefly unmaintained. While of course it’s great to have a vibrant ecosystem of lint checks, it’s problematic to make it more complex for users to wire these into their build system.

This proposal aims to solve these problems, specifically:

  • provide a way for TypeScript first-party diagnostics to produce Severity.WARNING while only displaying newly introduced ones to the developer
  • provide a way for third-parties to add new diagnostic-producing checks to the compiler, which can create diagnostics with any Severity
  • ensure that all lint-style checks can be hosted in tsc itself, so that users and build tool authors need only worry about tsconfig.json and the outputs of the standard TypeScript compiler

New command-line tool: tswarn

I propose a new bin script in the typescript package.json. I’ll call it tswarn in this doc, but the name could be many things:

  • tsvet (like govet)
  • tscheck

tswarn’s CLI looks similar to tsc, in that the user specifies a ts.Program and ts.CompilerOptions. However it also requires an additional input, which is a description of ranges of the files the user cares about. These ranges will be specified in the same format that the Prettier tool uses.

tswarn then runs the same compilation pipeline as tsc, but filters the results: it shows only the Severity.WARNING diagnostics which are reported against a location in a SourceFile that is within one of the user-supplied ranges.

We expect that users will not call tswarn directly. Instead, an intermediate layer such as precise-commits should first query the version control system to understand what ranges are under edit by the user, then encode this information and pass it to tswarn.

Even better, to get the same developer experience as we have within Google (and described by the CACM paper), the tooling can be called by a code review bot, and the results presented in the code review.

We do not recommend running tswarn as part of a continuous integration pipeline, because these warnings do not represent incorrect programs. The code review integration point is better because it gives the author and reviewer an opportunity to apply human judgement, and simply ignore some of the warnings if appropriate.

With the introduction of tswarn, TypeScript will have a first-class mechanism for handling Severity.WARNING diagnostics. We can therefore change some existing checks, such as –noUnusedLocals to produce Severity.WARNING diagnostics instead.

Editor plugins could use the ts.LanguageService to get these Severity.WARNING diagnostics, and could similarly filter them through the version control plugin, but this is out-of-scope of this proposal. The likely outcome is that editor plugins continue to show warnings on unmodified code (as they do with tslint today), which encourages some personality types to make unneeded changes to the code to “clean them up”. This is undesirable IMO but it’s not practical to make a separate Language Service for editors to interact with simply to do the filtering.

ts.DiagnosticPlugin

This is modelled after the ts.LanguageServicePlugin, and is already in use within Google’s Bazel typescript compiler. We propose to upstream this interface into TypeScript.

The source for our plugin API can be found here:

https://github.com/bazelbuild/rules_typescript/blob/a8479b33c611823482567b2fb83124370157976f/internal/tsc_wrapped/plugin_api.ts#L34

and the API is simply:

export interface TscPlugin { wrap(p: ts.Program, config?: {}): ts.Program; }

which follows the proxy design pattern.

This makes it easy to implement a plugin which reports additional diagnostics both into the Editor and on the command-line, by implementing both interfaces, proxying both the ts.Program and ts.LanguageService.

Each diagnostic produced by the plugin can also optionally include a list of CodeActions. These can be applied as the fixes for the issue. This is especially required for Severity.ERROR diagnostics, as these could never be enabled in a large codebase without also producing fixes that can be mechanically applied to avoid breaking the build. This can be thought of as providing "migrations" which users can run to get past breaking changes, whether the breaking change is

  • a new TypeScript version restricts the type system
  • user opts-into a new strictness check such as --strictFunctionTypes
  • a third-party ts.DiagnosticPlugin releases a new Severity.Error check

We will document that implementations are discouraged from checks which can be trivially fixed without developer intervention, such as whitespace or single vs. double quotes. These are better solved in a formatter tool such as Prettier. Such checks would also tend to produce conflicting Code Actions.

Configuring third-party plugins

We should probably have a way for users to specify plugins in their tsconfig.json files. This should simply be a new section that exactly matches the existing support for LanguageService plugins.

[CACM paper] https://cacm.acm.org/magazines/2018/4/226371-lessons-from-building-static-analysis-tools-at-google/pdf

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment