GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Number, enum, and boolean literal types by ahejlsberg · Pull Request #9407 · microsoft/TypeScript

Conversation

@ahejlsberg

@ahejlsberg Anders Hejlsberg (ahejlsberg) commented Jun 29, 2016 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

This PR expands upon #9163 by implementing number, enum, and boolean literal types and adding more comprehensive checking and type guard constructs for literal types. The PR supercedes #6196 and #7480, and fixes #2983, #6149, #6155, #7447, and #7642.

It is now possible to use string, numeric, and boolean literals (true and false) as literal types:

type Direction = -1 | 0 | 1;
type TrueOrFalse = true | false;
type Falsy = "" | 0 | false | null | undefined;

The predefined boolean type is now equivalent to the union type true | false.

When each member of an enum type has either an automatically assigned value, an initializer that specifies a numeric literal, or an initializer that specifies a single identifier naming another enum member, that enum type is considered a union enum type. The members of a union enum type can be used both as constants and as types, and the enum type is equivalent to a union of the declared member types.

const enum ShapeKind { Square, Rectangle, Circle }

type Shape =
    { kind: ShapeKind.Square; size: number; } |
    { kind: ShapeKind.Rectangle; width: number; height: number; } |
    { kind: ShapeKind.Circle; radius: number; };

All literal types as well as the null and undefined types are considered unit types. A unit type is a type that has only a single value.

Literal types have the following type relationships:

  • A string literal type is a subtype of and assignable to type string.
  • A numeric literal type is a subtype of and assignable to type number.
  • The boolean literal types true and false are subtypes of and assignable to type boolean.
  • A union enum member type is a subtype of and assignable to the containing enum type.

Certain expression locations are now considered literal type locations. In a literal type location, a literal expression has a literal type (e.g. "hello", 0, true, ShapeKind.Circle) instead of a regular type (e.g. string, number, boolean, ShapeKind). The following expression locations are considered literal type locations:

  • Operands of the ===, !==, ==, and != operators.
  • An expression in a case clause of a switch statement.
  • An expression within parentheses in a literal type location.
  • The true and false expressions of a conditional operator (?:) in a literal type location.
  • An expression that is contextually typed by a literal type or a union of one or more literal types.

When both operands of ===, !==, ==, or != are unit types or unions of unit types, the operands are checked using those types and an error is reported if the types are not comparable. Otherwise the operands are checked with their full types.

const foo: "foo" = "foo";
const bar: "bar" = "bar";
let s: string = "abc";
foo === bar;  // Error, "foo" and "bar" not comparable
foo === s;    // Ok
bar === s;    // Ok

The equality comparison operators now narrow each operand based on the type of the other operand. For example:

function f1(x: "foo" | "bar" | "baz") {
    if (x === "foo" || x === "bar") {
        x;  // "foo" | "bar"
    }
    else {
        x;  // "baz"
    }
}

function f2(x: string | boolean | null, y: string | number) {
    if (x === y) {
        x;  // string
        y;  // string
    }
    else {
        x;  // string | boolean | null
        y;  // string | number
    }
}

When all case expressions in a switch statement have unit types, the switch expression variable is narrowed in each case block and in the default block based on the listed cases. For example:

function f3(x: 0 | 1 | 2 | 3) {
    switch (x) {
        case 0:
            x;  // Type of x is 0
            break;
        case 1:
        case 2:
            x;  // Type of x is 1 | 2
            break;
        default:
            x;  // Type of x is 3
    }
}

For an equality, truthiness, or switch type guard on a reference x.y, we not only narrow x.y but also narrow x itself based on the narrowed type of x.y. For example:

type Result<T> = { success: true, value: T } | { success: false };

function foo(): Result<number> {
    if (someTest) {
        return { success: true, value: 42 };
    }
    else {
        return { success: false };
    }
}

function unwrap<T>(x: Result<T>) {
    switch (x.success) {
        case true:
            return x.value;
        case false:
            throw new Error("Missing value");
    }
}

let x = foo();
let y1 = x.success === true ? x.value : -1;  // Type guard allows x.value to be accessed
let y2 = !x.success ? -1 : x.value;
let y3 = x.success && x.value || -1;
let y4 = unwrap(x);

The && and || operators now understand that 0, false, and "" are falsy unit types and stricter typing of these operators is implemented as described here.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

zpdDG4gta8XKpMCd, yortus, gulbanana, chyld, davidk-zenefits, icanhasjonas, rexim, ForNeVeR, HerringtonDarkholme, Juszczak, and 49 more reacted with thumbs up emoji frederickfogerty, ciuncan, inad9300, and Glavin001 reacted with laugh emoji jesseschalken, zpdDG4gta8XKpMCd, ToucheSir, yortus, tinganho, andersekdahl, HerringtonDarkholme, Juszczak, rinukkusu, michaeljones, and 26 more reacted with hooray emoji zpdDG4gta8XKpMCd, yortus, jamesqo, ianks, Havvy, Juszczak, tmladek, jacobangel, enmanuelr, Eyas, and 20 more reacted with heart emoji
All reactions
  • 👍 59 reactions
  • 😄 4 reactions
  • 🎉 36 reactions
  • ❤️ 30 reactions
Anders Hejlsberg (ahejlsberg) added 13 commits June 18, 2016 07:21
@ahejlsberg
Numeric and boolean literal types
89596cb
@ahejlsberg
Accept new baselines
a204622
@ahejlsberg
Introduce literal type locations
a930917
@ahejlsberg
Accept new baselines
3ccd68b
@ahejlsberg
Literal types for const enum members
a91f714
@ahejlsberg
Accept new baselines
8455143
@ahejlsberg
Correct handling of falsy types ("" | 0 | false)
4774896
@ahejlsberg
Accept new baselines
770f423
@ahejlsberg
Make boolean assignable to union types containing both true and false
e900ebf
@ahejlsberg
Narrow unions of literal types by equality and truthiness
85128e0
@ahejlsberg
Accept new baselines
a2a4896
@ahejlsberg
Remove unused functions
e14fe5b
@ahejlsberg
Consider null, undefined, and void to be unit types in type guards
70e2c43
@msftclas Microsoft Contribution License Agreements (msftclas) added the cla-not-required label Jun 29, 2016
This was referenced Jun 29, 2016
Boolean literal types and return type propagation for generators #2983
Closed
unreachable code detection for switch cases out of range of string literal types #6149
Closed
consider allowing exhaustive switch statement #6155
Closed
make the strict equality comparison operator a typeguard for string literal types #7447
Closed
Include const enum members as possible literal types #7642
Closed
@Quantumplation Pi Lanningham (Quantumplation) mentioned this pull request Jun 29, 2016
Intersection types and string literals #9410
Closed
@Eyas

Eyas (Eyas) commented Jun 29, 2016

Copy link
Copy Markdown
Contributor

What about

("foo" === "bar"); // is this an error in this case, since a literal
                   // is inferred as a literal type at that location?

These are similar to the "this branch is always true"/false warnings you'd get in some languages (which we now have more of with --stictNullChecks I believe(?)). I'm still torn about whether they're errors or warnings..

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Jun 29, 2016

Copy link
Copy Markdown
Member Author

Eyas (@Eyas) Yes, "foo" === "bar" is an error since the two string literals have incompatible literal types. This error is effectively the mechanism for detecting misspelled string literals. For example, see discussion in #6149.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@weswigham

Wesley Wigham (weswigham) commented Jun 30, 2016

Copy link
Copy Markdown
Member

Comments based on my experience testing my PR (which you should steal tests from):

I don't see any NaN or (+/-)Infinity numeric types - should they be there? NaN at least probably should - and it should be one of the Falsy types (and needs to be handled like 0). NaN also potentially needs to have some really funky and unique comparability rules - as NaN === NaN is false. Additionally, parsing -Infinity in a type position is likely to throw a wrench in the current literal type parsing scheme.

Are equivalent numbers with different text equivalent, eg 0x1 vs 1 vs 0o1 vs 01 (octal literals are super fun), and do they preserve their original text? I don't see anything in the type relationship arithmetic to account for this textual inequality (yet value equality).

svieira, yahiko00, and mihailik reacted with thumbs up emoji
All reactions
  • 👍 3 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@gcnew Marin Marinov (gcnew) mentioned this pull request Jun 30, 2016
Allow method return types to end with a digit microsoft/TypeScript-TmLanguage#199
Closed
@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Jun 30, 2016

Copy link
Copy Markdown
Member Author

Wesley Wigham (@weswigham) Unless someone has a compelling scenario, I'm strongly inclined not to include NaN and Infinity as literal types. Literal types intended for scenarios where you pick a (small) set of specific values to match on in type guards and overloads, and I simply can't imagine meaningful uses of NaN or Infinity as a discriminant values or literal values for overloading.

Yes, 1, 0x1, 1.0, and 1e0 are all the same type, but the canonical representation is always used when displaying types (i.e. the string you get by converting the number to text using "" + value).

I'll look at poaching some tests.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Anders Hejlsberg (ahejlsberg) added 3 commits July 1, 2016 09:18
@ahejlsberg
Ensure const enum members with same value have same type identity
d37d50a
@ahejlsberg
Accept new baselines
a91a293
@ahejlsberg
Detect always truthy and always falsy types with &&, ||, ! operators
881d510
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Jul 3, 2016
Union Types And Strings #9489
Closed
@ahejlsberg
All types can be null/undefined and thus falsy in regular type checki…
1cec8dc
…ng mode
Anders Hejlsberg (ahejlsberg) added 2 commits July 24, 2016 07:28
@ahejlsberg
Merge branch 'master' into literalTypes
ff0cbb5
# Conflicts:
#	src/compiler/checker.ts
@ahejlsberg
Optimize type inference
a1a8725
@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) mentioned this pull request Jul 25, 2016
Design Meeting Notes, 07/22/2016 #9935
Closed
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Jul 26, 2016
Attempt to fix #9919 - Intersecting discriminated union breaks type narrowing #9939
Closed
Anders Hejlsberg (ahejlsberg) added 2 commits July 27, 2016 17:10
@ahejlsberg
Display enum member types using qualified names
a5fcd5f
@ahejlsberg
Accept new baselines
5ff07dc
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Jul 28, 2016
Trade-offs in Control Flow Analysis #9998
Open
@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Jul 28, 2016

Copy link
Copy Markdown
Member Author

The RWC baseline differences look good except for a few cases where our optimistic control flow analysis is fooled by hidden side effects in function calls. We're discussing that in #9998. Otherwise I think we're finally ready to merge this PR.

zpdDG4gta8XKpMCd, HerringtonDarkholme, Lenne231, and yahiko00 reacted with thumbs up emoji zpdDG4gta8XKpMCd, yortus, and MattiasBuelens reacted with hooray emoji
All reactions
  • 👍 4 reactions
  • 🎉 3 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg
Anders Hejlsberg (ahejlsberg) merged commit 0c131fa into master Jul 28, 2016
@zpdDG4gta8XKpMCd

ZpdDG4gta (zpdDG4gta8XKpMCd) commented Jul 28, 2016

Copy link
Copy Markdown

🎉 thank you for the hard work!

aluanhaddad reacted with hooray emoji
All reactions
  • 🎉 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

This was referenced Jul 28, 2016
Infer string literals at comparison locations #6196
Closed
Prototype: Numeric Literal Types #7480
Closed
@tetsuharuohzeki Tetsuharu Ohzeki (tetsuharuohzeki) mentioned this pull request Jul 29, 2016
Use boolean literal types to represent our type option-t/option-t#152
Merged
This was referenced Jul 29, 2016
BC break for method call on union type #10025
Closed
RegExpMatchArray has lost some compatibility with Array since 2.1.0-dev.20160729 #10041
Closed
Exhaustive switch on a type union with a default case yields TS2339: Property 'x' does not exist on type 'never' #9838
Closed
@gcnew Marin Marinov (gcnew) mentioned this pull request Aug 11, 2016
(suggestion) tagged union types #10253
Closed
@benjamin-hodgson Benjamin Hodgson (benjamin-hodgson) mentioned this pull request Aug 19, 2016
Bug: Boolean type literals misbehaving #10432
Closed
@kitsonk

Kitson Kelly (kitsonk) commented Aug 29, 2016

Copy link
Copy Markdown
Contributor

Mohamed Hegazy (@mhegazy) this doesn't have a milestone and isn't listed on the Roadmap... Is this going into TypeScript 2.0 or do we have to wait until TypeScript 2.1?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mhegazy Mohamed Hegazy (mhegazy) added this to the TypeScript 2.0.1 milestone Aug 29, 2016
@mhegazy
Mohamed Hegazy (mhegazy) deleted the literalTypes branch August 29, 2016 22:24
@mhegazy

Mohamed Hegazy (mhegazy) commented Aug 29, 2016

Copy link
Copy Markdown
Contributor

it is a 2.0-RC feature. added milestone. and updated the roadmap.

kitsonk and joaolucasl reacted with thumbs up emoji kitsonk reacted with hooray emoji
All reactions
  • 👍 2 reactions
  • 🎉 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@kitsonk Kitson Kelly (kitsonk) mentioned this pull request Aug 30, 2016
Literal enums #10604
Closed
@bcherny Boris Cherny (bcherny) mentioned this pull request Sep 3, 2016
Add ts enums bcherny/json-schema-to-typescript#15
Merged
@kitsonk Kitson Kelly (kitsonk) mentioned this pull request Oct 3, 2016
Type string does not narrow when checked for equality with a string literal. #11306
Closed
@olivr70 Olivier Chevet (olivr70) mentioned this pull request Apr 11, 2017
NaN, Infinity and -Infinity not accepted in number literal types #15135
Closed
@mhegazy Mohamed Hegazy (mhegazy) mentioned this pull request Oct 19, 2017
Type narrowing with non-literal const enums #19321
Closed
@unekinn Une Sofie Kinn Ekroll (unekinn) mentioned this pull request Mar 7, 2018
TypeScript: Exhaustiveness checing for enums breaks with EnumHandling.String RicoSuter/NJsonSchema#647
Closed
@abenhamdine Arnaud Benhamdine (abenhamdine) mentioned this pull request Apr 24, 2018
Postgres infinity timestamps SweetIQ/schemats#91
Closed
@microsoft 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.

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

TypeScript 2.0.2

Development

Successfully merging this pull request may close these issues.

Boolean literal types and return type propagation for generators

Uh oh!

There was an error while loading. Please reload this page.

10 participants

@ahejlsberg @Eyas @weswigham @DanielRosenwasser @gcnew @zpdDG4gta8XKpMCd @mhegazy @CyrusNajmabadi @kitsonk @msftclas
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.