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 previouslystring | string[]
and now is alwaysstring[]
—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 acceptsboolean | 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 returnsnumber
—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 usebind
orcall
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
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.
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.