Skip to content

Instantly share code, notes, and snippets.

@RyanCavanaugh
Created May 29, 2020 13:45
Show Gist options
  • Save RyanCavanaugh/f80f9ddc50d45c4d76e7c4101efada28 to your computer and use it in GitHub Desktop.
Save RyanCavanaugh/f80f9ddc50d45c4d76e7c4101efada28 to your computer and use it in GitHub Desktop.

Pedantic Mode

Background

In TypeScript 2.3, we introduced the --strict flag. The intent of this flag was to "opt in" to the strictest possible subset of TypeScript, along with the implied future breaking changes associated with any further improvements in strictness. As we added new forms of stricter checking, we added opt-out flags so that developers could disable individual aspects of this "strictest" mode.

However, pursuant to our goal of not making overly large breaking changes, we've held off on implementing stricter checking in places where it would cause an infeasibly large number of breaks in exixting codebases. Even developers who opted into --strict should not find themselves facing hundreds or thousands of new errors during a routine compiler version upgrade.

This has restricted our ability to add certain forms of stricter checking where the desired errors occur in places that are either highly idiomatic, or would require new syntax across a large number of use sites.

For example, we know that including undefined in the type of array access expressions arr[i] is a highly-demanded feature, but even a cursory check of existing code shows that this would cause hundreds of errors in almost any codebase.

Another example would be the override keyword - this is a popular request, but the feature provides very little value without a corresponding ability to require that all overriding methods are declared as such. It would be untenable, even in strict, to expect people upgrading from version N to N+0.1 of TypeScript to also insert new override keywords throughout their codebase.

Proposal

The proposal is effectively a non-feature -- let's decide to allow adding new forms of strictness that are not part of --strict. The value provided here is fairly uncontroversial; we simply need to decide how to name and message these features in a way that minimizes user confusion.

Naming

How should pedantic flags be named? Several options here:

  • Apply a naming convention e.g. --pedanticNoImplicitStringConversion
  • Leave them as "bare" switches, e.g. --noImplicitStringConversion, which aren't enabled by default by --strict
  • Something else?

Points in favor of the "bare" convention:

  • Avoids the appearance of judgmentalism (i.e. "We don't think you should, but okay")
  • In the event we find a way to make something the default under --strict, the migration path is clearer
  • Shorter

Points in favor of the "pedantic" convention:

  • Makes our opinion on the flag quite clear (i.e. "We don't think you should, but okay")
  • Extremely clear that these are not part of --strict

Should --pedantic exist?

--strict exists to turn on all flags starting with strict, so should --pedantic exist?

I would argue against doing this. Developers inclined toward the "safest possible" TypeScript will be strongly motivated to turn on such a switch if it exists, and will surely advocate for others to do the same. This will result in future upgrade pain for very little concrete upside.

By definition, we don't think these are switches that can be painlessly enabled, so the expected user experience under --pedantic would be:

  • Upgrade to the next TypeScript version
  • Hundreds of new errors appear
  • Oh no; are any of these actual breaks, or just new pedantic warnings?
  • Go read the release blog post, find out which new pedantic flags were added
  • Disable the new pedantic rules
  • Get the build to clean again
  • Possibly re-enable each pedantic flag on a case-by-case basis

If we don't have --pedantic, developers who want the "safest possible" experience would encounter this workflow:

  • Upgrade to the next TypeScript version
  • Get the build to clean again
  • Go read the release blog post, find out which new pedantic flags were added
  • Possibly enable each pedantic flag on a case-by-case basis

This is just a shorter version of the steps under --pedantic; nothing of value was gained by having the flag exist.

Candidate Pedantic Switches

Note: Some of these lack canonical issues; flag names are extremely open to bikeshedding

  • --noImplicitBooleanCoercion #7746
  • --noImplicitStringCoercion #7989
  • --pessimisticLookups #13778
  • --pessimisticControlFlow #9998 ??
  • --noImplicitPropertyLookup to turn off allowing expr.prop when expr just has a string index signature
  • --noImplicitOverride if override were to exist
  • --noAnyPropertyAccess variant of safeAny
  • --noUntypedFunctionCalls untyped function calls are a huge trap and should maybe just be disallowed under --strict

We should still take care to implement these in a way that doesn't meaningfully explode the configuration space -- none of these rules above would change subtype or assignability relations, for example, so could be safely implemented without needing to ensure compat with existing type definitions.

@docwilco
Copy link

docwilco commented Oct 9, 2023

I disagree with your statement that it's less effort just because the text of your list is shorter.

First off, lots of people have multiple projects and CI running for them. It's quite easy to have both a strict build and a pedantic build. You don't require pedantic to succeed, but you use it as an automated bit of information that things have changed. It does not require staying on top of TypeScript, which not everybody does, because it's not the main development language for everyone.

It's easy to go back from pedantic to strict, it's not so easy to follow all the releases of all the tools you use. So I would say do go for a pedantic and let people discover new flags automatically.

To echo your lists.

With pedantic and a proper CI setup:

  • Wait for something to break
  • See what new flag is breaking it
  • Decide whether or not to fix it

Without:

  • Get notified about every release
  • Read the notes for every release
  • Manually update every release

@docwilco
Copy link

docwilco commented Oct 9, 2023

And on top of that, if the error message explains which flag is responsible for it, people would have a pretty easy time figuring things out, or turning it off.

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