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 — Control flow analysis of aliased conditional expressions and discriminants by ahejlsberg · Pull Request #44730 · microsoft/TypeScript

Conversation

@ahejlsberg

@ahejlsberg Anders Hejlsberg (ahejlsberg) commented Jun 24, 2021 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Member

With this PR we support control flow analysis of conditional expressions and discriminant property accesses indirectly referenced through const variables. For example:

function f1(x: unknown) {
    const isString = typeof x === 'string';
    if (isString) {
        x.length;  // Ok
    }
}

function f2(obj: { kind: 'foo', foo: string } | { kind: 'bar', bar: number }) {
    const isFoo = obj.kind === 'foo';
    if (isFoo) {
        obj.foo;  // Ok
    }
    else {
        obj.bar;  // Ok
    }
}

function f3(obj: { kind: 'foo', foo: string } | { kind: 'bar', bar: number }) {
    const { kind } = obj;
    if (kind === 'foo') {
        obj.foo;  // Ok
    }
    else {
        obj.bar;  // Ok
    }
}

function f4(obj: { kind: 'foo', foo: string } | { kind: 'bar', bar: number }) {
    const k = obj.kind;
    if (k === 'foo') {
        obj.foo;  // Ok
    }
    else {
        obj.bar;  // Ok
    }
}

Intuitively, indirect references to conditional expressions or discriminant property accesses behave exactly as if the expressions were written in-line.

Narrowing through indirect references occurs only when the conditional expression or discriminant property access is declared in a const variable declaration with no type annotation, and the reference being narrowed is a const variable, a readonly property, or a parameter for which there are no assignments in the function body.

Some examples where narrowing does not occur (but it would had the indirectly referenced conditions been written in-line):

function f5(x: unknown) {
    let isString = typeof x === 'string';
    if (isString) {
        x.length;  // Error, no narrowing because isString isn't 'const'
    }
}

function f6(obj: { kind: 'foo', foo: string } | { kind: 'bar', bar: number }) {
    const isFoo = obj.kind === 'foo';
    obj = obj;
    if (isFoo) {
        obj.foo;  // Error, no narrowing because 'obj' is assigned in function body
    }
}

Up to five levels of indirection are analyzed in conditional expressions. For example, two levels of indirections are analyzed in the following code:

function f7(x: string | number | boolean) {
    const isString = typeof x === "string";
    const isNumber = typeof x === "number";
    const isStringOrNumber = isString || isNumber;
    if (isStringOrNumber) {
        x;  // string | number
    }
    else {
        x;  // boolean
    }
}

It is possible to mix indirect conditional expressions and directly specified conditions:

function f8(value: number | undefined) : number {
    const isNumber = typeof value == "number";
    if (isNumber && value > 0) {
        return value * 10;
    }
    return 0;
}

This PR fixes most of the long list of issues that were closed as duplicates of #12184, but not all. In particular, the pattern of destructuring a discriminant property and a payload property into two local variables and expecting a coupling between the two is not supported as the control flow analyzer doesn't "see" the connection. For example:

type Data = { kind: 'str', payload: string } | { kind: 'num', payload: number };

function foo({ kind, payload }: Data) {
    if (kind === 'str') {
        payload.length;  // Error, payload not narrowed to string
    }
}

We may be able to support that pattern later, but likely not in this PR.

Fixes #12184.
Fixes #19421.
Fixes #19943.
Fixes #24865.
Fixes #26804.
Fixes #31037.
Fixes #31059.
Fixes #31291.
Fixes #31344.
Fixes #31870.
Fixes #33192.
Fixes #34535.
Fixes #35603.
Fixes #36510.
Fixes #37638.
Fixes #37855.
Fixes #39657.
Fixes #39996.
Fixes #40201.
Fixes #40341.
Fixes #41266.
Fixes #41870.
Fixes #43333.

Sorry, something went wrong.

Uh oh!

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

Kingwl, Nokel81, eugene-stativka, matijagrcic, IllusionMH, jo3-l, Ameobea, karol-majewski, Jack-Works, mmis1000, and 214 more reacted with thumbs up emoji Kingwl, acutmore, neo, PrinceRajRoy, JoshuaKGoldberg, Dsan10s, JVictorV, lautarodragan, RyanCavanaugh, vdumbrav, and 14 more reacted with laugh emoji Kingwl, r-cyr, Nokel81, kincaidoneil, dested, AlCalzone, Ayc0, amcsi, martpie, sandinmyjoints, and 113 more reacted with hooray emoji Kingwl, alexrock, idoros, Nokel81, colinux, eugene-stativka, matijagrcic, jo3-l, berndartmueller, Ameobea, and 135 more reacted with heart emoji Kingwl, AviVahl, Nokel81, saulshanabrook, Ayc0, amcsi, eugene-stativka, Ameobea, fvictorio, irinakk, and 72 more reacted with rocket emoji Kingwl, neo, afoures, JVictorV, p-j, itsMapleLeaf, laymonage, madiodio, ovidb, anuraghazra, and 4 more reacted with eyes emoji
All reactions
  • 👍 224 reactions
  • 😄 24 reactions
  • 🎉 123 reactions
  • ❤️ 145 reactions
  • 🚀 82 reactions
  • 👀 14 reactions
Anders Hejlsberg (ahejlsberg) added 2 commits June 24, 2021 10:27
@ahejlsberg
CFA inlining of conditional expressions referenced by const variables
08b6e2e
@ahejlsberg
Accept new baselines
0bed057
@typescript-bot TypeScript Bot (typescript-bot) assigned Anders Hejlsberg (ahejlsberg) Jun 24, 2021
@typescript-bot TypeScript Bot (typescript-bot) added Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Jun 24, 2021
@ahejlsberg
Anders Hejlsberg (ahejlsberg) requested review from Ryan Cavanaugh (RyanCavanaugh) and Wesley Wigham (weswigham) June 24, 2021 18:05
@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Jun 24, 2021

Copy link
Copy Markdown
Member Author

TypeScript Bot (@typescript-bot) test this
TypeScript Bot (@typescript-bot) user test this
TypeScript Bot (@typescript-bot) run dt
TypeScript Bot (@typescript-bot) perf test this

All reactions

Sorry, something went wrong.

Uh oh!

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

@typescript-bot

TypeScript Bot (typescript-bot) commented Jun 24, 2021 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the parallelized community code test suite on this PR at 0bed057. You can monitor the build here.

All reactions

Sorry, something went wrong.

Uh oh!

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

@typescript-bot

TypeScript Bot (typescript-bot) commented Jun 24, 2021 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the perf test suite on this PR at 0bed057. You can monitor the build here.

Update: The results are in!

All reactions

Sorry, something went wrong.

Uh oh!

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

@typescript-bot

TypeScript Bot (typescript-bot) commented Jun 24, 2021 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the parallelized Definitely Typed test suite on this PR at 0bed057. You can monitor the build here.

All reactions

Sorry, something went wrong.

Uh oh!

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

@typescript-bot

TypeScript Bot (typescript-bot) commented Jun 24, 2021 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the extended test suite on this PR at 0bed057. You can monitor the build here.

All reactions

Sorry, something went wrong.

Uh oh!

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

@Kingwl

Wenlu Wang (Kingwl) commented Jun 24, 2021 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Contributor

Cooooooooooooooool!!!!!! Great work!!!!!

All reactions

Sorry, something went wrong.

Uh oh!

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

@typescript-bot

TypeScript Bot (typescript-bot) commented Jun 24, 2021

Copy link
Copy Markdown
Contributor

Anders Hejlsberg (@ahejlsberg)
The results of the perf run you requested are in!

Here they are:

Comparison Report - main..44730

Metric main 44730 Delta Best Worst
Angular - node (v10.16.3, x64)
Memory used 344,407k (± 0.02%) 344,459k (± 0.02%) +52k (+ 0.01%) 344,275k 344,590k
Parse Time 1.79s (± 0.57%) 1.80s (± 0.59%) +0.01s (+ 0.73%) 1.78s 1.84s
Bind Time 0.84s (± 0.48%) 0.84s (± 0.66%) +0.00s (+ 0.36%) 0.83s 0.85s
Check Time 5.19s (± 0.50%) 5.34s (± 0.60%) +0.15s (+ 2.89%) 5.27s 5.41s
Emit Time 5.67s (± 1.49%) 5.61s (± 0.61%) -0.06s (- 0.99%) 5.55s 5.70s
Total Time 13.49s (± 0.83%) 13.60s (± 0.45%) +0.11s (+ 0.79%) 13.49s 13.73s
Compiler-Unions - node (v10.16.3, x64)
Memory used 201,023k (± 0.03%) 201,308k (± 0.06%) +286k (+ 0.14%) 200,914k 201,504k
Parse Time 0.78s (± 0.71%) 0.79s (± 0.63%) +0.00s (+ 0.38%) 0.78s 0.80s
Bind Time 0.53s (± 1.10%) 0.53s (± 1.23%) +0.00s (+ 0.19%) 0.51s 0.54s
Check Time 7.55s (± 0.59%) 7.88s (± 0.74%) +0.33s (+ 4.33%) 7.80s 8.07s
Emit Time 2.29s (± 1.01%) 2.28s (± 1.50%) -0.01s (- 0.26%) 2.23s 2.38s
Total Time 11.15s (± 0.48%) 11.48s (± 0.71%) +0.33s (+ 2.92%) 11.36s 11.73s
Monaco - node (v10.16.3, x64)
Memory used 340,445k (± 0.01%) 340,467k (± 0.02%) +22k (+ 0.01%) 340,317k 340,638k
Parse Time 1.46s (± 0.51%) 1.46s (± 0.65%) -0.00s (- 0.07%) 1.43s 1.47s
Bind Time 0.74s (± 0.81%) 0.74s (± 1.10%) +0.01s (+ 1.09%) 0.72s 0.76s
Check Time 5.33s (± 0.49%) 5.45s (± 0.85%) +0.12s (+ 2.21%) 5.34s 5.53s
Emit Time 3.02s (± 1.01%) 2.99s (± 0.55%) -0.03s (- 0.99%) 2.95s 3.03s
Total Time 10.54s (± 0.29%) 10.63s (± 0.54%) +0.10s (+ 0.93%) 10.52s 10.75s
TFS - node (v10.16.3, x64)
Memory used 303,727k (± 0.02%) 303,726k (± 0.02%) -1k (- 0.00%) 303,586k 303,809k
Parse Time 1.17s (± 0.51%) 1.19s (± 0.59%) +0.01s (+ 1.19%) 1.18s 1.21s
Bind Time 0.71s (± 0.71%) 0.71s (± 0.47%) +0.00s (+ 0.71%) 0.70s 0.72s
Check Time 4.85s (± 0.57%) 4.97s (± 0.49%) +0.12s (+ 2.50%) 4.91s 5.03s
Emit Time 3.14s (± 1.57%) 3.14s (± 1.52%) +0.01s (+ 0.29%) 3.01s 3.23s
Total Time 9.86s (± 0.58%) 10.02s (± 0.58%) +0.15s (+ 1.52%) 9.87s 10.15s
material-ui - node (v10.16.3, x64)
Memory used 471,419k (± 0.01%) 471,361k (± 0.01%) -59k (- 0.01%) 471,261k 471,470k
Parse Time 1.72s (± 0.42%) 1.73s (± 0.74%) +0.01s (+ 0.81%) 1.70s 1.76s
Bind Time 0.66s (± 0.61%) 0.66s (± 1.06%) +0.00s (+ 0.30%) 0.64s 0.67s
Check Time 14.14s (± 0.47%) 14.12s (± 0.51%) -0.02s (- 0.13%) 13.97s 14.33s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 16.51s (± 0.39%) 16.51s (± 0.48%) -0.00s (- 0.02%) 16.33s 16.72s
Angular - node (v12.1.0, x64)
Memory used 322,463k (± 0.11%) 322,767k (± 0.03%) +304k (+ 0.09%) 322,599k 323,001k
Parse Time 1.78s (± 0.67%) 1.78s (± 0.49%) +0.00s (+ 0.28%) 1.76s 1.80s
Bind Time 0.83s (± 0.58%) 0.83s (± 0.80%) +0.00s (+ 0.36%) 0.82s 0.85s
Check Time 5.11s (± 0.61%) 5.23s (± 0.40%) +0.12s (+ 2.33%) 5.18s 5.28s
Emit Time 5.85s (± 1.07%) 5.88s (± 0.78%) +0.02s (+ 0.41%) 5.79s 6.04s
Total Time 13.57s (± 0.58%) 13.72s (± 0.47%) +0.15s (+ 1.13%) 13.64s 13.95s
Compiler-Unions - node (v12.1.0, x64)
Memory used 188,537k (± 0.07%) 188,783k (± 0.10%) +246k (+ 0.13%) 188,275k 189,034k
Parse Time 0.77s (± 0.84%) 0.77s (± 0.77%) +0.01s (+ 0.78%) 0.77s 0.79s
Bind Time 0.53s (± 0.42%) 0.53s (± 0.92%) +0.00s (+ 0.95%) 0.52s 0.54s
Check Time 7.05s (± 0.43%) 7.36s (± 0.75%) +0.31s (+ 4.35%) 7.27s 7.51s
Emit Time 2.33s (± 0.81%) 2.35s (± 1.26%) +0.03s (+ 1.20%) 2.29s 2.42s
Total Time 10.67s (± 0.31%) 11.03s (± 0.48%) +0.35s (+ 3.30%) 10.94s 11.21s
Monaco - node (v12.1.0, x64)
Memory used 323,543k (± 0.01%) 323,528k (± 0.06%) -15k (- 0.00%) 322,733k 323,711k
Parse Time 1.41s (± 0.66%) 1.42s (± 0.65%) +0.01s (+ 0.99%) 1.40s 1.44s
Bind Time 0.71s (± 0.63%) 0.72s (± 0.31%) +0.01s (+ 0.98%) 0.71s 0.72s
Check Time 5.17s (± 0.27%) 5.30s (± 0.66%) +0.14s (+ 2.65%) 5.24s 5.42s
Emit Time 3.07s (± 0.93%) 3.08s (± 0.61%) +0.01s (+ 0.33%) 3.05s 3.14s
Total Time 10.35s (± 0.29%) 10.52s (± 0.42%) +0.17s (+ 1.64%) 10.44s 10.65s
TFS - node (v12.1.0, x64)
Memory used 288,402k (± 0.01%) 288,418k (± 0.02%) +16k (+ 0.01%) 288,304k 288,547k
Parse Time 1.19s (± 0.69%) 1.19s (± 0.73%) +0.00s (+ 0.08%) 1.17s 1.21s
Bind Time 0.69s (± 0.53%) 0.69s (± 0.89%) +0.01s (+ 0.73%) 0.68s 0.70s
Check Time 4.76s (± 0.58%) 4.90s (± 0.55%) +0.13s (+ 2.81%) 4.86s 4.96s
Emit Time 3.19s (± 1.09%) 3.19s (± 0.87%) -0.00s (- 0.03%) 3.14s 3.24s
Total Time 9.83s (± 0.54%) 9.96s (± 0.55%) +0.13s (+ 1.37%) 9.87s 10.08s
material-ui - node (v12.1.0, x64)
Memory used 449,982k (± 0.06%) 449,993k (± 0.06%) +10k (+ 0.00%) 448,924k 450,201k
Parse Time 1.71s (± 0.35%) 1.71s (± 0.63%) -0.00s (- 0.18%) 1.69s 1.74s
Bind Time 0.64s (± 1.16%) 0.64s (± 1.06%) +0.00s (+ 0.62%) 0.63s 0.66s
Check Time 12.64s (± 0.39%) 12.77s (± 0.54%) +0.13s (+ 1.03%) 12.62s 12.90s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 14.99s (± 0.34%) 15.13s (± 0.48%) +0.13s (+ 0.88%) 14.99s 15.28s
Angular - node (v14.15.1, x64)
Memory used 321,281k (± 0.01%) 321,351k (± 0.01%) +70k (+ 0.02%) 321,278k 321,394k
Parse Time 1.79s (± 0.56%) 1.81s (± 0.74%) +0.02s (+ 1.34%) 1.78s 1.84s
Bind Time 0.87s (± 0.57%) 0.88s (± 0.86%) +0.01s (+ 1.15%) 0.87s 0.90s
Check Time 5.08s (± 0.32%) 5.22s (± 0.43%) +0.14s (+ 2.77%) 5.19s 5.28s
Emit Time 5.88s (± 0.57%) 5.91s (± 0.53%) +0.03s (+ 0.58%) 5.85s 5.96s
Total Time 13.62s (± 0.35%) 13.82s (± 0.36%) +0.20s (+ 1.48%) 13.73s 13.96s
Compiler-Unions - node (v14.15.1, x64)
Memory used 189,136k (± 0.62%) 187,846k (± 0.38%) -1,290k (- 0.68%) 187,466k 190,729k
Parse Time 0.80s (± 0.50%) 0.80s (± 0.74%) +0.00s (+ 0.50%) 0.79s 0.82s
Bind Time 0.56s (± 0.61%) 0.56s (± 0.85%) 0.00s ( 0.00%) 0.55s 0.57s
Check Time 7.17s (± 0.46%) 7.50s (± 0.95%) +0.33s (+ 4.63%) 7.39s 7.71s
Emit Time 2.29s (± 0.72%) 2.31s (± 0.69%) +0.03s (+ 1.09%) 2.28s 2.35s
Total Time 10.81s (± 0.35%) 11.18s (± 0.78%) +0.37s (+ 3.40%) 11.02s 11.44s
Monaco - node (v14.15.1, x64)
Memory used 322,490k (± 0.01%) 322,526k (± 0.01%) +36k (+ 0.01%) 322,489k 322,599k
Parse Time 1.47s (± 0.39%) 1.48s (± 0.75%) +0.01s (+ 0.75%) 1.47s 1.52s
Bind Time 0.75s (± 1.02%) 0.75s (± 0.50%) -0.00s (- 0.13%) 0.74s 0.75s
Check Time 5.14s (± 0.67%) 5.27s (± 0.33%) +0.12s (+ 2.37%) 5.23s 5.31s
Emit Time 3.11s (± 1.03%) 3.11s (± 0.63%) +0.00s (+ 0.06%) 3.08s 3.16s
Total Time 10.47s (± 0.54%) 10.61s (± 0.31%) +0.14s (+ 1.31%) 10.56s 10.69s
TFS - node (v14.15.1, x64)
Memory used 287,388k (± 0.00%) 287,420k (± 0.01%) +32k (+ 0.01%) 287,363k 287,465k
Parse Time 1.25s (± 0.79%) 1.26s (± 0.95%) +0.01s (+ 0.64%) 1.24s 1.29s
Bind Time 0.71s (± 0.42%) 0.71s (± 0.42%) 0.00s ( 0.00%) 0.71s 0.72s
Check Time 4.79s (± 0.32%) 4.89s (± 0.52%) +0.10s (+ 2.04%) 4.85s 4.96s
Emit Time 3.26s (± 0.62%) 3.28s (± 0.61%) +0.01s (+ 0.43%) 3.23s 3.32s
Total Time 10.03s (± 0.30%) 10.14s (± 0.29%) +0.12s (+ 1.19%) 10.07s 10.21s
material-ui - node (v14.15.1, x64)
Memory used 448,227k (± 0.06%) 448,276k (± 0.03%) +49k (+ 0.01%) 447,664k 448,438k
Parse Time 1.75s (± 0.44%) 1.75s (± 0.42%) -0.00s (- 0.06%) 1.73s 1.77s
Bind Time 0.69s (± 0.48%) 0.69s (± 0.58%) +0.00s (+ 0.14%) 0.68s 0.70s
Check Time 12.83s (± 0.67%) 12.86s (± 0.43%) +0.03s (+ 0.20%) 12.75s 12.96s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 15.27s (± 0.56%) 15.30s (± 0.32%) +0.03s (+ 0.20%) 15.20s 15.39s
System
Machine Namets-ci-ubuntu
Platformlinux 4.4.0-206-generic
Architecturex64
Available Memory16 GB
Available Memory1 GB
CPUs4 × Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
Hosts
  • node (v10.16.3, x64)
  • node (v12.1.0, x64)
  • node (v14.15.1, x64)
Scenarios
  • Angular - node (v10.16.3, x64)
  • Angular - node (v12.1.0, x64)
  • Angular - node (v14.15.1, x64)
  • Compiler-Unions - node (v10.16.3, x64)
  • Compiler-Unions - node (v12.1.0, x64)
  • Compiler-Unions - node (v14.15.1, x64)
  • Monaco - node (v10.16.3, x64)
  • Monaco - node (v12.1.0, x64)
  • Monaco - node (v14.15.1, x64)
  • TFS - node (v10.16.3, x64)
  • TFS - node (v12.1.0, x64)
  • TFS - node (v14.15.1, x64)
  • material-ui - node (v10.16.3, x64)
  • material-ui - node (v12.1.0, x64)
  • material-ui - node (v14.15.1, x64)
Benchmark Name Iterations
Current 44730 10
Baseline main 10

Developer Information:

Download Benchmark

All reactions

Sorry, something went wrong.

Uh oh!

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

weswigham
Wesley Wigham (weswigham) reviewed Jun 24, 2021
View reviewed changes

@weswigham Wesley Wigham (weswigham) left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation-wise, this looks OK (is there a reason for specifically limiting the nesting analysis to two levels?); but I think the new tests have been elided, since you have a bunch of examples in the OP, but there's no new tests showing the functionality. (I'm interested in some with readonly props, both static and instance, since those don't have examples in your OP, but are referenced in some comments, even though later there's a check for exactly VariableDeclarations)

Sorry, something went wrong.

Uh oh!

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

All reactions
Comment thread src/compiler/checker.ts
break;
case SyntaxKind.CommaToken:
return narrowType(type, expr.right, assumeTrue);
// Ordinarily we won't see && and || expressions in control flow analysis because the Binder breaks those

@weswigham Wesley Wigham (weswigham) Jun 24, 2021

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about ternary expressions?

Sorry, something went wrong.

Uh oh!

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

All reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) Jun 24, 2021

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We narrow within the branches of ternary expressions, but not based on the result of ternary expressions, so I don't think there is anything to do there.

Sorry, something went wrong.

Uh oh!

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

All reactions
@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Jun 24, 2021

Copy link
Copy Markdown
Member Author

is there a reason for specifically limiting the nesting analysis to two levels?

I'd like to have a fixed limit so we can avoid maintaining a stack of inlined symbols that would otherwise be needed (to catch accidentally recursive definitions). Two levels is rather arbitrary, could make it three or even five.

I think the new tests have been elided

Yeah, haven't added tests yet.

All reactions

Sorry, something went wrong.

Uh oh!

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

@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Jun 24, 2021
Indirect type narrowing via const #12184
Closed
@RyanCavanaugh

Ryan Cavanaugh (RyanCavanaugh) commented Jun 24, 2021

Copy link
Copy Markdown
Member

TypeScript Bot (@typescript-bot) pack this

All reactions

Sorry, something went wrong.

Uh oh!

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

@typescript-bot

TypeScript Bot (typescript-bot) commented Jun 24, 2021 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Contributor

Heya Ryan Cavanaugh (@RyanCavanaugh), I've started to run the tarball bundle task on this PR at 0bed057. You can monitor the build here.

All reactions

Sorry, something went wrong.

Uh oh!

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

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Jun 24, 2021

Copy link
Copy Markdown
Member Author

I'm interested in some with readonly props, both static and instance, since those don't have examples in your OP, but are referenced in some comments, even though later there's a check for exactly VariableDeclarations

The isVariableDeclaration checks are all related to the declaration of the aliased condition or discriminant access, and are unrelated to how the referenced being narrowed was declared. But sure, we should have tests for all different kinds.

matijagrcic reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

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

@typescript-bot

TypeScript Bot (typescript-bot) commented Jun 24, 2021 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Contributor

Hey Ryan Cavanaugh (@RyanCavanaugh), I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/105495/artifacts?artifactName=tgz&fileId=94FC84B182999B7D6DCA47117848030F812A9A364841F767F760CB8D5DBAAF0402&fileName=/typescript-4.4.0-insiders.20210624.tgz"
    }
}

and then running npm install.


There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/[email protected]".;

All reactions

Sorry, something went wrong.

Uh oh!

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

@IllusionMH

Andrii Dieiev (IllusionMH) commented Jun 24, 2021 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Contributor

Is it possible to also add narrowing for constants from same destructurization?

function test(data: { type: "num", value: number } | { type: "str", value: string }) {
  const { type, value } = data;
  if (type === "num") {
    value.toFixed(); // currently error
    data.value.toFixed(); // ok
  } else {
    value.charAt(0); // currently error
    data.value.charAt(0); // ok
  }
}

UPD. As mentioned in edit I've missed - maybe later, but not in this PR.

ArnaudBarre and sambecker reacted with thumbs up emoji gregor-mueller, andreas-soroko, patroza, ibezkrovnyi, zyhou, metdockal, and niklavskatlaps reacted with eyes emoji
All reactions
  • 👍 2 reactions
  • 👀 7 reactions

Sorry, something went wrong.

Uh oh!

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

@noseratio

Andrew Nosenko (noseratio) commented Jun 25, 2021

Copy link
Copy Markdown

Anders Hejlsberg (@ahejlsberg), would that be possible to extend this for classes as well, e.g.:

class T {
    s = "";
}

function f1(x: unknown) {
    const isT = x instanceof T;
    if (isT) {
        x.s;  // Ok
    }
}

Also, maybe to include some other type checks, e.g., const isString = x?.constructor === String, which works for both primitive string values and instances of String class.

All reactions

Sorry, something went wrong.

Uh oh!

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

@acutmore

Ashley Claymore (acutmore) commented Jun 25, 2021 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Contributor

Anders Hejlsberg (@ahejlsberg), would that be possible to extend this for classes as well, e.g.:

class T {
    s = "";
}

function f1(x: unknown) {
    const isT = x instanceof T;
    if (isT) {
        x.s;  // Ok
    }
}

Already working https://www.staging-typescript.org/play?ts=4.4.0-pr-44730-11#code/MYGwhgzhAEAq0G8BQ1XRgXmgImwbiQF8kkAzAVwDtgAXASwHtLpSBGACgA8AuaKga0oMA7pQCUiFGmBMINaHQjwsnBZTlhqAUwak4BNAr3tFsCckOHOAOgh5UAegfQA8vympihIA ❤️

noseratio, JofArnold, tonivj5, and MarchewkaMatthew reacted with thumbs up emoji noseratio, JofArnold, and tonivj5 reacted with rocket emoji
All reactions
  • 👍 4 reactions
  • 🚀 3 reactions

Sorry, something went wrong.

Uh oh!

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

@noseratio

Andrew Nosenko (noseratio) commented Jun 25, 2021 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown

Already working

Very cool! Interestingly, I can use object instead of unknown and it still works :)

All reactions

Sorry, something went wrong.

Uh oh!

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

@fatcerberus

Bruce Pascoe (fatcerberus) commented Jun 25, 2021

Copy link
Copy Markdown

Narrowing through indirect references occurs only when the conditional expression or discriminant property access is declared in a const variable declaration with no type annotation, and the reference being narrowed is a const variable, a readonly property, or a parameter for which there are no assignments in the function body.

Should I take this mean narrowing won't work on non-readonly properties? That limits the utility of this IMO; I pretty much never use the readonly keyword in my interfaces.

All reactions

Sorry, something went wrong.

Uh oh!

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

Anders Hejlsberg (ahejlsberg) added 2 commits June 25, 2021 09:40
@ahejlsberg
Add tests
ad81160
@ahejlsberg
Accept new baselines
82c601e
weswigham
Wesley Wigham (weswigham) approved these changes Jun 25, 2021
View reviewed changes

@weswigham Wesley Wigham (weswigham) left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would still be nice to have tests documenting our behavior for other const-ish variable types (eg, static read-only fields) which alias a guard, if only to say we don't currently support it.

Sorry, something went wrong.

Uh oh!

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

All reactions
@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Jun 25, 2021

Copy link
Copy Markdown
Member Author

Should I take this mean narrowing won't work on non-readonly properties? That limits the utility of this IMO; I pretty much never use the readonly keyword in my interfaces.

Correct. For example, the following fails if data isn't marked readonly:

function test(obj: { readonly data: string | number }) {
    const isString = typeof obj.data === 'string';
    if (isString) {
        let s: string = obj.data;
    }
}

The rationale here is that we can't know for certain that data hasn't been modified since the condition was evaluated. We'd effectively have to check that there are no assignments to obj.data between the declaration of isString and the reference to isString in the if statement. This adds complexity and could be prohibitively expensive.

It does of course work if you copy the mutable property into a const local:

function test(obj: { data: string | number }) {
    const { data } = obj;
    const isString = typeof data === 'string';
    if (isString) {
        let s: string = data;
    }
}
simonkarman and WhiterBlack reacted with thumbs up emoji brainkim reacted with confused emoji brainkim reacted with eyes emoji
All reactions
  • 👍 2 reactions
  • 😕 1 reaction
  • 👀 1 reaction

Sorry, something went wrong.

Uh oh!

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

@IllusionMH Andrii Dieiev (IllusionMH) mentioned this pull request Aug 16, 2021
Aliased Conditions with explicit type definition results in the "old" behavior #45469
Open
@andrewbranch Andrew Branch (andrewbranch) mentioned this pull request Aug 19, 2021
Type guard by deep property #38839
Closed
This was referenced Aug 25, 2021
checker.ts :: checkModuleDeclaration(): Redundant type guard erroneously narrows node type to 'never' #45572
Closed
checker.ts: explicit boolean type annotation overrides type guard, silencing an error #45594
Closed
@ruizb Benoit Ruiz (ruizb) mentioned this pull request Aug 29, 2021
Incorrect object properties type inference with aliased conditional expressions #45624
Closed
@karlhorky

Karl Horky (karlhorky) commented Aug 30, 2021

Copy link
Copy Markdown
Contributor

Cross-linking to #30581.

Thanks Joe Calzaretta (@jcalz) ! Correlated unions sound cool!

Anders Hejlsberg (@ahejlsberg) would this be the correct issue to watch for this feature of "coupling" between destructured properties / elements?

All reactions

Sorry, something went wrong.

Uh oh!

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

@MartinJohns Martin Johns (MartinJohns) mentioned this pull request Aug 31, 2021
control flow analysis incorrectly narrows type to never when type-guarded #45664
Closed
@IllusionMH Andrii Dieiev (IllusionMH) mentioned this pull request Sep 30, 2021
Alias type conditions to desctrutured variables #46143
Closed
5 tasks done
@acid-chicken Acid Chicken (acid-chicken) mentioned this pull request Oct 3, 2021
refactor: publishHogeStreamとStreamのEventEmitterに型定義する misskey-dev/misskey#7769
Merged
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Oct 18, 2021
Control flow analysis of aliased conditions is not able to narrow object properties #46412
Closed
5 tasks done
@Egor-Koldasov Egor Koldasov (Egor-Koldasov) mentioned this pull request Nov 24, 2021
Implicit type guard by other variables are not always working #46915
Closed
@equt equt mentioned this pull request Mar 7, 2022
Typescript checking fails when ternary operator logic is extracted #48146
Closed
@MartinJohns Martin Johns (MartinJohns) mentioned this pull request Mar 13, 2022
Runtime Validation With Variables Fails #48241
Closed
@MartinJohns Martin Johns (MartinJohns) mentioned this pull request Apr 30, 2022
Type validations do not carry over when assigned to a const #48891
Closed
@JeanMeche

Matthieu Riegler (JeanMeche) commented Jul 15, 2022

Copy link
Copy Markdown
Contributor

Anders Hejlsberg (@ahejlsberg)
Could it maybe possible to reflect on the type that is hold that predicate behaviour ?

That would be to prevent that kind of weird behaviour !

All reactions

Sorry, something went wrong.

Uh oh!

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

@whzx5byb whzx5byb mentioned this pull request Aug 2, 2022
Discriminated union parameter destructuring doesn't work if the fields have defaults #50139
Open
@whzx5byb whzx5byb mentioned this pull request Aug 18, 2022
TypeScript doesn't assume the value type in a ternary operator when it has been assigned before if the value is an object key #50356
Closed
Zzzen
Zzzen (Zzzen) reviewed Sep 11, 2022
View reviewed changes
Comment thread src/compiler/checker.ts
case SyntaxKind.AmpersandAmpersandToken:
return assumeTrue ?
narrowType(narrowType(type, expr.left, /*assumeTrue*/ true), expr.right, /*assumeTrue*/ true) :
getUnionType([narrowType(type, expr.left, /*assumeTrue*/ false), narrowType(type, expr.right, /*assumeTrue*/ false)]);

@Zzzen Zzzen (Zzzen) Sep 11, 2022

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When assumeTrue is false, theoretically expr.right is reachable if and only if expr.left is truthy?

-getUnionType([narrowType(type, expr.left, /*assumeTrue*/ false), narrowType(type, expr.right, /*assumeTrue*/ false)])
+getUnionType([narrowType(type, expr.left, /*assumeTrue*/ false), narrowType(narrowType(type, expr.left, /*assumeTrue*/ true), expr.right, /*assumeTrue*/ false)])

Sorry, something went wrong.

Uh oh!

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

All reactions
@MartinJohns Martin Johns (MartinJohns) mentioned this pull request Dec 5, 2022
Adding explicit type that is the same as the inferred type prevents narrowing later on #51757
Closed
@fatcerberus Bruce Pascoe (fatcerberus) mentioned this pull request Jan 18, 2023
Capture type info in a variable's type #52289
Closed
@SteveRusin XxX (SteveRusin) mentioned this pull request Mar 15, 2023
allow storing results of narrowing of class properties in booleans for further narrowing #53267
Closed
@andrewbranch Andrew Branch (andrewbranch) mentioned this pull request Sep 1, 2023
Type inference fails when using combined boolean variable in if condition #55577
Closed
@MartinJohns Martin Johns (MartinJohns) mentioned this pull request Nov 12, 2023
false positive error after null checking with typed boolean variable instead non typed variable #56369
Closed
@MartinJohns Martin Johns (MartinJohns) mentioned this pull request Mar 12, 2024
"Smarter" look ahead logic for variable existing #57733
Closed
6 tasks done
@jet2jet jet (ジェット) (jet2jet) mentioned this pull request May 23, 2024
Invalid type inference for optional chaining operator when nullish check is extracted to const varaiable #58632
Closed
@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) mentioned this pull request Oct 10, 2024
Adding boolean type assertion changes type narrowing #60190
Closed
@MartinJohns Martin Johns (MartinJohns) mentioned this pull request Jan 2, 2025
Assertions do not narrow related variables like conditions do #60904
Closed
@MartinJohns Martin Johns (MartinJohns) mentioned this pull request Jan 12, 2025
TypeScript does not narrow variable that cannot be undefined #60958
Closed
@erictraut Eric Traut (erictraut) mentioned this pull request May 5, 2025
Mypy could not do condition inference python/mypy#19029
Closed
@microsoft Microsoft (microsoft) locked as resolved and limited conversation to collaborators Oct 21, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

@weswigham Wesley Wigham (weswigham) weswigham approved these changes
@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) RyanCavanaugh approved these changes
+1 more reviewer
@Zzzen Zzzen (Zzzen) Zzzen left review comments
Reviewers whose approvals may not affect merge requirements

Uh oh!

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

Assignees

@ahejlsberg Anders Hejlsberg (ahejlsberg)

Labels

Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

None yet

Milestone

TypeScript 4.4.0 (Beta)

Development

Successfully merging this pull request may close these issues.

Destructing assignment fail to implicit cast type allow storing results of narrowing in booleans for further narrowing Type guard does not work when its value is set to a variable. Feature Request: Infer types when using variable assignment with if-statements. Indirect type narrowing via const
Issue narrowing tagged unions with temporary const A problem with type checking in if statement. TypeScript should respect "instanceof" in logical expressions Bug diagnosing error "Object is possibly 'undefined'" Suggestion: Discriminate between union types based on extracted property

Uh oh!

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

18 participants

@ahejlsberg @typescript-bot @Kingwl @RyanCavanaugh @IllusionMH @noseratio @acutmore @fatcerberus @ruizb @slorber @balazsorban44 @Roaders @MarcosAtApprentice @jcalz @karlhorky @JeanMeche @weswigham @Zzzen
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.