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 — Fix discriminant property check by ahejlsberg · Pull Request #29110 · microsoft/TypeScript

Conversation

@ahejlsberg

@ahejlsberg Anders Hejlsberg (ahejlsberg) commented Dec 20, 2018

Copy link
Copy Markdown
Member

In #27695 we were slightly too permissive in what we consider a discriminant property. Specifically, we required the combined union type to contain at least one unit type, but that unit type could have occurred in combination with non-unit types. We now check that at least one underlying property has a pure unit type or union of unit types.

Fixes #29106.

Sorry, something went wrong.

Uh oh!

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

All reactions
Anders Hejlsberg (ahejlsberg) added 3 commits December 20, 2018 07:17
@ahejlsberg
Fix discriminant property check
93acaac
@ahejlsberg
Add regression test
3e93461
@ahejlsberg
Accept new baselines
017c11a
@ahejlsberg
Anders Hejlsberg (ahejlsberg) requested review from Daniel Rosenwasser (DanielRosenwasser) and Wesley Wigham (weswigham) December 20, 2018 15:31
@weswigham

Wesley Wigham (weswigham) commented Dec 20, 2018

Copy link
Copy Markdown
Member

Based on your comment and the repro, isn't the real issue that we don't combine discriminant narrowing with other generated facts in control flow well?

All reactions

Sorry, something went wrong.

Uh oh!

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

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Dec 20, 2018

Copy link
Copy Markdown
Member Author

Wesley Wigham (@weswigham) Well, yes and no. We would do better if we didn't have the design limitation that causes us to reset narrowed types for properties when the shape of an object changes because of a discriminant check, and we might in fact not have this issue. But it would require the ability to "record and replay" preceding narrowing checks, and that would definitely complicate the control flow analyzer. And, either way, there's really no point in considering a and b discriminant properties in the example.

All reactions

Sorry, something went wrong.

Uh oh!

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

@weswigham

Wesley Wigham (weswigham) commented Dec 20, 2018

Copy link
Copy Markdown
Member

In the given example, sure, but I can't help but feel that someone somewhere has taken a dependency on a property of string | undefined being discriminable since support was added.

All reactions

Sorry, something went wrong.

Uh oh!

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

@jack-williams

Jack Williams (jack-williams) commented Dec 20, 2018

Copy link
Copy Markdown
Collaborator

Anders Hejlsberg (@ahejlsberg) When you say:

reset narrowed types for properties when the shape of an object changes because of a discriminant check

What does shape mean: changing the apparent properties aggregated across the union type, or something else?

Would it be too difficult (or wrong) to test for this change of shape, rather than classifying a and b as non-discriminable? Narrowing U, where type U = A | B; never changes shape and therefore the narrowing does not need to be reset.

All reactions

Sorry, something went wrong.

Uh oh!

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

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Dec 20, 2018

Copy link
Copy Markdown
Member Author

Wesley Wigham (@weswigham) Discriminants are meant to be unit types and we only narrow based on equality and truthiness checks. With a discriminant of string | undefined you'd potentially be able to narrow based on obj.x === undefined, but you wouldn't be able to narrow based on a string value or a typeof check. Just doesn't seem useful, it didn't work before, and I can't imagine anyone taking a dependency on that in the last few weeks.

All reactions

Sorry, something went wrong.

Uh oh!

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

@weswigham

Wesley Wigham (weswigham) commented Dec 21, 2018

Copy link
Copy Markdown
Member

With a discriminant of string | undefined you'd potentially be able to narrow based on obj.x === undefined, but you wouldn't be able to narrow based on a string value or a typeof check. Just doesn't seem useful, it didn't work before, and I can't imagine anyone taking a dependency on that in the last few weeks.

A typeof-based narrowing works juts fine for string | undefined.

All reactions

Sorry, something went wrong.

Uh oh!

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

@weswigham

Wesley Wigham (weswigham) commented Dec 21, 2018 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Member

Also 2-3 months != 2 weeks. This has been out for awhile (and has shipped in stable versions of TS).

All reactions

Sorry, something went wrong.

Uh oh!

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

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Dec 21, 2018 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Member Author

Wesley Wigham (@weswigham) No, we don't do discriminant based narrowing for typeof checks:

type Foo = { a: string, x: number } | { a: 0, y: number };

function xxx(obj: Foo) {
  if (typeof obj.a === "string") {
    obj.x;  // Error (because we don't narrow here)
  }
  if (obj.a === 0) {
    obj.y;  // Ok (discriminant based narrowing)
  }
}

With the fix in this PR our behavior changes in the following because we no longer consider a a discriminant property:

type Foo = { a: string | undefined, x: number } | { a: string, y: number };

function xxx(obj: Foo) {
  if (obj.a === undefined) {
    obj.x;  // Previously would narrow, now doesn't
  }
}

As I said, I can't imagine anyone having taken a dependency on this in the last few weeks (or months or whatever).

This PR is the right fix for the issue given the design constraints we currently have.

All reactions

Sorry, something went wrong.

Uh oh!

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

@jack-williams

Jack Williams (jack-williams) commented Dec 21, 2018

Copy link
Copy Markdown
Collaborator

If I understand the issue properly it seems to be a tradeoff between

  1. Not adding this fix: breaking code for people using u.a && u.b && f(u.a, u.b); who have not upgraded to 3.2 yet.

  2. Adding this fix: breaking code for early adopters narrowing on undefined. There probably are a few, as this example was in the original thread on nullable discriminants.

The number of people affected by (1) seems likely to be larger than (2), and (2) can be fixed by replacing the equality test with a type guard.

All reactions

Sorry, something went wrong.

Uh oh!

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

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Dec 21, 2018

Copy link
Copy Markdown
Member Author

Jack Williams (@jack-williams) Yes, that's the tradeoff. But #2 is actually even further qualified: Breaking code for early adopters of discriminant narrowing involving properties that have no single underlying property of a pure unit type or union of unit types.

jack-williams 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.

@ahejlsberg
Anders Hejlsberg (ahejlsberg) merged commit beebda3 into master Dec 31, 2018
@ahejlsberg
Anders Hejlsberg (ahejlsberg) deleted the fixDiscriminantCheck branch December 31, 2018 03:46
@nekolab Yi S. (nekolab) mentioned this pull request Aug 16, 2024
Make type narrowing for destructured discriminated unions work for more types #59657
Open
6 tasks done
@microsoft Microsoft (microsoft) locked as resolved and limited conversation to collaborators Oct 21, 2025
@typescript-bot TypeScript Bot (typescript-bot) assigned Anders Hejlsberg (ahejlsberg) Oct 21, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) Awaiting requested review from DanielRosenwasser
@weswigham Wesley Wigham (weswigham) Awaiting requested review from weswigham

Uh oh!

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

Assignees

@ahejlsberg Anders Hejlsberg (ahejlsberg)

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

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

3 participants

@ahejlsberg @weswigham @jack-williams
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.