Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mutability widening for literal types #6554

Closed
wants to merge 22 commits into from

Conversation

DanielRosenwasser
Copy link
Member

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:

  1. String literal types come in two flavors: fresh and stale (neither is "better" - think day old pizza).
  2. Two string literal types with the same content are always equal, regardless of freshess.
  3. A string literal _always_ starts off with a fresh string literal type, regardless of contextual typing.

Changes to the widening process are as follows:

  1. Variable-like declarations with no type annotations now go through an intermediate mutability-aware widening process.
  2. The standard widening process now widens all fresh literal types to string.

Mutability-aware widening is fairly simple:

  1. For mutable bindings (like var and let declarations), a singleton literal type is widened to string, regardless of freshness.
  2. For immutable bindings (like const declarations):
    1. A string literal type is stripped of freshness.
    2. A union's type's string literal type constituents are stripped of freshness.

Changes to best-common-type selection are as follows:

  1. If all constituents are string-like, then the resulting type is string.

@DanielRosenwasser
Copy link
Member Author

This looks kind of untenable to review. I'm gonna rebase this so people can go commit by commit.

All string literals will now have their types inferred as string literal types with an associated "freshness".
When a string is bound to a "read-only" declaration, freshness associated with the type is lost during widening.
When a string is bound to a mutable declaration, it is unconditionally widened to 'string.
In any other context where widening takes place, any fresh string literal type is widened to 'string.
…at contextual typing can succeed.

Fixes breakage found in 'tests/cases/fourslash/quickInfoForOverloadOnConst1.ts'
@DanielRosenwasser
Copy link
Member Author

I've rebased so there are batches of commits. Here's the order

  1. Changes to the type checker.
  2. Baseline acceptances for symbols, types, and then error & output files.
  3. Addition of new tests.
  4. Baseline acceptance of for tests.
  5. Correction of fourslash tests.

@JsonFreeman
Copy link
Contributor

What flavor of string literal type does a string type literal introduce?

Does a readonly property initialization strip a string literal of its freshness?

@DanielRosenwasser
Copy link
Member Author

What flavor of string literal type does a string type literal introduce?

Fresh. Thanks for the question, I've amended the issue.

Does a readonly property initialization strip a string literal of its freshness?

At this point I believe that will be the case, but I will need to discuss it with @ahejlsberg.

@DanielRosenwasser DanielRosenwasser added the Domain: Literal Types Unit types including string literal types, numeric literal types, Boolean literals, null, undefined label Jan 22, 2016
@DanielRosenwasser
Copy link
Member Author

This was nice to experiment with. 😃

@mhegazy mhegazy deleted the literalTypeWidening branch November 2, 2017 21:02
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Domain: Literal Types Unit types including string literal types, numeric literal types, Boolean literals, null, undefined
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants