Non-breaking changes

In each of these cases, some user code becomes superfluous, but it neither fails to type-check nor causes any runtime errors.

Symbols

A change to an exported symbol is not breaking when:

  • a symbol is exported which was not previously exported and which does not share a name with another symbol which was previously exported

Interfaces, Type Aliases, and Classes

Any change to a non-user-constructible type is not breaking when:

  • a new optional property is added to the type, since all existing code will continue working (playground)

  • a readonly object property on the type becomes a more specific ("narrower") type, for example if it was previously string | string[] and now is always string[]—since all user code will continue working and type-checking (playground). Note that this includes a previously-optional property becoming required.1

  • a new required property is added to the object—since its presence does not require the consuming code to use the property at a type level (playground)2

Functions

For functions which return or accept user-constructible types, the rules specified for Non-breaking Changes: Interfaces, Type Aliases, and Classes hold. Otherwise, a change to a function declaration is not breaking when:

  • a function (including a class method or constructor) accepts a less specific ("wider") type, for example if it previously accepted only a boolean but now accepts boolean | undefined—since all existing user code will continue working and type-checking (playground)

  • a function (including a class method) which returns a more specific ("narrower") type, for example if it previously returned number | undefined and now always returns number—since all user code will continue working and type-checking (playground)

  • a function (including a class constructor or method) makes a previously-required argument optional—since all existing user code will continue to work with it (playground)

  • changing a function from an arrow function declaration to function declaration, since it allows the caller to use bind or call meaningfully where they could not before. (Note that at the time of authoring, current TypeScript version 4.3, such a change introduces parameter bivariance. This is not breaking, but may be undesirable.)

Notes

1

Strictly speaking, one value may stop being comparable to another value in this scenario. However, this is both a rare edge case and fits under the standard rule where changes which simply let users delete now-defunct code are allowed.

2

Mostly, anyway—see Appendix C: On Variance in TypeScript – Higher-order type operations for a discussion of how the in operator (or similar operations discriminating between unions) makes this cause breakage in some cases. These are rare enough, and easily-enough solved, that the rule remains workable.