Mutability widening for literal types by DanielRosenwasser · Pull Request #6554 · microsoft/TypeScript (original) (raw)
This PR changes the way in which string literal types work today. It seeks to address some of the major issues brought up in #6167. The basic gist is that all string literals start off with string literal types and are appropriately widened to string
at appropriate binding locations. This widening to string
occurs when the binding itself is mutable.
A alternative (and competing) approach exists on #6196, where we infer string literal types only at select locations.
Here's a small sample of the current behavior:
const HelloKind = "Hello"; // type: "Hello" let GoodbyeKind = "Goodbye"; // type: string
const Kind_A = HelloKind; // type: "Hello" let Kind_B = HelloKind; // type: string
// type: { name: string; } const Foo = { name: "Foo", };
interface Bar { barProp: "Bar"; } const myBar: Bar;
// type: "Bar" const { barProp } = myBar;
// type: string const { baz } = { baz: "bar" };
However, when working with a union type of string literals, we won't automatically widen to string
even for a mutable binding:
declare function f(): "Foo" | "Bar";
// type: "Foo" | "Bar" let a = f();
Fundamental changes to string literals and string literal types are:
- String literal types come in two flavors: fresh and stale (neither is "better" - think day old pizza).
- Two string literal types with the same content are always equal, regardless of freshess.
- A string literal _always_ starts off with a fresh string literal type, regardless of contextual typing.
Changes to the widening process are as follows:
- Variable-like declarations with no type annotations now go through an intermediate mutability-aware widening process.
- The standard widening process now widens all fresh literal types to
string
.
Mutability-aware widening is fairly simple:
- For mutable bindings (like
var
andlet
declarations), a singleton literal type is widened tostring
, regardless of freshness. - For immutable bindings (like
const
declarations):- A string literal type is stripped of freshness.
- A union's type's string literal type constituents are stripped of freshness.
Changes to best-common-type selection are as follows:
- If all constituents are string-like, then the resulting type is
string
.