镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Object property in anonymous function in typeguard: Should the typeguard work when the object is const and property is readonly? #9511

Description

TypeScript Version: 2.0.0-dev.20160704
Code

const x : { readonly a: string | undefined } = { a: 's' };
if (typeof x.a !== 'undefined') {
    () => x.a.length;
  }

Expected behavior:
x.a.length should be valid, since x.a is in a typeguard, x is constant, and x.a is readonly, and thus value of x.a cannot change even when the function is called at a later time.

Actual behavior:
[ts] Object is possibly 'undefined'.
(property) a: string | undefined

Activity

  1. zpdDG4gta8XKpMCd commented on Jul 5, 2016

    @zpdDG4gta8XKpMCd

    only true for x.a being a primitive (string, number, boolean, null, undefined) won't work for arrays and objects, Date, Refex

  2. added this to the milestone on Jul 6, 2016
  3. sandersn commented on Jul 28, 2016

    @sandersn
    Member

    Aleksey-Bykov what can change with non-primitives? For example,

    interface I { 
      p: string | undefined; 
      q: number;
    }
    const x: { readonly a: I | undefined } = { a: { p: undefined, q: 12 } };
    let f: () => number;
    if (typeof x.a !== 'undefined') {
      f = () => x.a.q;
    }

    How can I change x.a to make the property access x.a.q invalid?

  4. sandersn commented on Jul 28, 2016

    @sandersn
    Member

    Fix is up for review at #10015.

  5. weswigham commented on Jul 28, 2016

    @weswigham
    Member

    Nathan Shively-Sanders (@sandersn) :

    const x = {
      get foo(): {x: number, y: number} | undefined {
        return this._foo;
      },
      setFoo(foo: any) {
        this._foo = foo;
      }
    };
    
    x.setFoo({x: 42, y: 42});
    if (typeof x.foo !== "undefined") {
      setTimeout(() => console.log(Math.sqrt((x.foo.x ** 2) + (x.foo.y **2)));
    }
    x.setFoo(undefined);

    Since getters with an unpaired setter are readonly, no further annotations are required. readonly isn't the same thing as immutable, and conflating the two could cause issues (beyond the aliasing ones we've already discussed).

    On the other hand, it was always possibly to write a getter which deleted its own value on access and pass that around pretending it was a plain old object, thereby invalidating any type guards on it; so maybe getters are just awful and don't really matter.

  6. ahejlsberg commented on Jul 29, 2016

    @ahejlsberg
    Member

    I'm not 100% sure about this change. readonly just means that you can't assign to the property, but there is no guarantee that consecutive get operations will return the same value.

  7. sandersn commented on Aug 2, 2016

    @sandersn
    Member

    checkIdentifier already uses isReadonlySymbol to decide whether to narrow outside the lambda. Is that incorrect? I guessed that it was allowed because getters were kind of holey and we decided to be less strict (and less correct) in this case.

    (as a reminder, isReadonlySymbol includes readonly, const, get-only accessors and enum members).

  8. modified the milestones: TypeScript 2.1, on Aug 16, 2016
  9. modified the milestones: TypeScript 2.1, , on Oct 27, 2016
  10. added
    Working as IntendedThe behavior described is the intended behavior; this is not a bug
    and removed
    BugA bug in TypeScript
    on Sep 17, 2018
  11. ahejlsberg commented on Sep 17, 2018

    @ahejlsberg
    Member

    Closing per our decision in the design meeting.

  12. locked as resolved and limited conversation to collaborators on Oct 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Working as IntendedThe behavior described is the intended behavior; this is not a bug

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions