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

Non-null assertion operator and the typeof operator #17370

Description

TypeScript Version: 2.4.1

Code

// strictNullChecks: true

let x : string | undefined
let y = x!

type t1 = typeof x!
type t2 = typeof y

Expected behavior:
the t1 statement should create a type of type string.
the t2 statement should create a type of type string.

Actual behavior:
the t1 statement errors with ';' semicolon expected.
the t2 statement creates a type of type string.

There appears to be no way to achieve this without defining a separate variable, or by type guarding to remove the undefined / null.

With strict null checks on, this is especially troublesome in the case of generics with extends clauses, i.e.

class Foo {}
class Bar<T extends Foo> {}

const x : Foo | undefined

const y = new Bar<typeof x>() // throws an exception because typeof x === Foo | undefined != Foo

It would be great if the non-null assertion operator worked natively in typeof statements, or even if you could use brackets to resolve the assertion before the typeof.

Activity

  1. jcalz commented on Jul 24, 2017

    @jcalz
    Contributor

    I think you're asking for #6606. For now, workarounds involve forcing the type checker to evaluate the type you care about while not causing much extra work at runtime:

    class Foo {}
    class Bar<T extends Foo> {}
    declare const x : Foo | undefined
    if (false as true && (typeof x !== 'undefined')) {
        var definedX = x; // assignment never happens at runtime
    }
    const y = new Bar<typeof definedX>()
  2. bradzacher commented on Jul 24, 2017

    @bradzacher
    ContributorAuthor

    i mean, yeah #6606 would definitely solve this as it's a pretty all encompassing proposition...

    It would be nice if there was a better error message to signify that the error is specifically that the expression isn't supported, rather than a 'semicolon expected' error.

  3. KiaraGrouwstra commented on Jul 24, 2017

    @KiaraGrouwstra
    Contributor

    A potential alternative here would be for the compiler to expose ! on the type level. I'm not yet aware of outstanding proposals for that though.

  4. Igorbek commented on Jul 24, 2017

    @Igorbek
    Contributor

    @tycho01 back to the #6606 discussion, is it going to be too many type-level operators, isn't? that was one of my points, we'll never get same expressiveness.

  5. KiaraGrouwstra commented on Jul 24, 2017

    @KiaraGrouwstra
    Contributor

    I'd be interested to get their take on it. Evidently they didn't feel the expression level had too many operators to add that !.
    Personally my ideal situation would be to keep the two completely on-par. But yeah, the TS team may well be considerably more conservative.

    Edit: to get to your point, yeah, if we are to be stuck with a type level not on par with the expression level, that admittedly goes to validate the added value of your expression-first #6606 variant.

  6. RyanCavanaugh commented on Jul 24, 2017

    @RyanCavanaugh
    Member

    We can expand the grammar here bit by bit without going full-blast with #6606. This incremental change seems pretty reasonable

  7. bradzacher commented on Jul 24, 2017

    @bradzacher
    ContributorAuthor

    it would be great as (right now) there's no other way to scrub null/undefined at the expression level; making it more difficult to work with the strictNullChecks option at times.

  8. Igorbek commented on Jul 24, 2017

    @Igorbek
    Contributor

    Ryan Cavanaugh (@RyanCavanaugh) Hm, I'd prefer full-blast (you might have already got this 😄) as it would cover much more cases at once.

  9. KiaraGrouwstra commented on Jul 25, 2017

    @KiaraGrouwstra
    Contributor

    Brad Zacher (@bradzacher): I think you meant type level. :)

  10. KiaraGrouwstra commented on Jul 25, 2017

    @KiaraGrouwstra
    Contributor

    Ryan Cavanaugh (@RyanCavanaugh): on #6606, does the reluctance stem from perceived complexity of the implementation as you proposed it (link)? Because afaik, the proposal would not necessarily* bring breaking changes.

    *: ignoring the keyword priority discussion raised by Igorbek for the expression-based proposal.

  11. RyanCavanaugh commented on Jul 25, 2017

    @RyanCavanaugh
    Member

    #6606 is accepting PRs and anyone can jump in, but we're not ready to commit team resources to it yet. The prioritization is based on a cost/benefit analysis: adding ! to the grammar here is probably a ~1/2-day work item (for someone on the team) but that other proposal is probably a 5-day or more work item (due to likely unknown design aspects that would only become apparent during implementation) plus a lot of ongoing complexity maintenance cost.

  12. Igorbek commented on Jul 25, 2017

    @Igorbek
    Contributor

    Ryan Cavanaugh (@RyanCavanaugh) is #6606 accepting PRs even in expression-based syntax?

  13. KiaraGrouwstra commented on Jul 25, 2017

    @KiaraGrouwstra
    Contributor

    On-topic, with 6606 a type-level ! alternative could look like this:

    class Foo {}
    // expression level function application today, to test:
    declare function assert<T>(v: T | null | undefined): T;
    let a: Foo | undefined;
    let b = assert(a); // Foo
    // v type level function 'application' with #6606:
    type Assert<T> = (<U>(v: U | null | undefined) => U)(T);
    let x: Assert<Foo | undefined>; // Foo
  14. bradzacher commented on Jul 25, 2017

    @bradzacher
    ContributorAuthor

    from the looks of it - typescript doesn't let you have self invoking functions like that within a type declaration.

    [ts] ';' expected.
    [ts] Cannot find name 'T'.
    
  15. KiaraGrouwstra commented on Jul 26, 2017

    @KiaraGrouwstra
    Contributor

    Brad Zacher (@bradzacher): yeah, the function 'application' there is not possible yet. Either flavor of the 6606 proposal could enable something similar to that.

  16. KiaraGrouwstra commented on Jul 26, 2017

    @KiaraGrouwstra
    Contributor

    Ryan Cavanaugh (@RyanCavanaugh) is #6606 accepting PRs even in expression-based syntax?

    since we're just about down to aesthetics... welcome to typeof fn(1 as any as MyType)!

  17. KiaraGrouwstra commented on Aug 12, 2017

    @KiaraGrouwstra
    Contributor

    This topic came up in the recent design meeting at #17621 from the duplicate issue #14366.

  18. KiaraGrouwstra commented on Aug 21, 2017

    @KiaraGrouwstra
    Contributor

    I made some initial progress at #17948, but still broken.

  19. KiaraGrouwstra commented on Sep 7, 2017

    @KiaraGrouwstra
    Contributor

    Igor Oleinikov (@Igorbek):

    @tycho01 back to the #6606 discussion, is it going to be too many type-level operators, isn't? that was one of my points, we'll never get same expressiveness.

    Seems they reconsidered in favor of your conclusion:

    We have discussed this in a few design meetings now, and do not feel that adding a new type operator is the right way to go.
    Type operators come with a maintainability cost, as well as learning cost by adding a new concept to the language.
    An alternative here is to always remove null|undefined in type positions. this allows the use case in #14366 to follow as expected.

  20. KiaraGrouwstra commented on Sep 9, 2017

    @KiaraGrouwstra
    Contributor

    Working example at 4d14c9f.

  21. mhegazy commented on Mar 2, 2018

    @mhegazy
    Contributor

    This should be doable today with NonNullable type.

  22. bradzacher commented on Mar 4, 2018

    @bradzacher
    ContributorAuthor

    Mohamed Hegazy (@mhegazy) it should be yeah! 👍

    I would have liked the operator for it for consistency with non-type def syntax, but I understand and agree with the design decisions.

    I see it was merged a few weeks ago. I'd assume that'll be part of the next minor version (2.8??).
    do you know when that is being released?

  23. mhegazy commented on Mar 8, 2018

    @mhegazy
    Contributor

    do you know when that is being released?

    TypeScript 2.8 should be available by the end of March.

  24. mhegazy commented on Mar 9, 2018

    @mhegazy
    Contributor

    Closing as fixed by NonNullable type.

  25. added
    FixedA PR has been merged for this issue
    and removed on Mar 9, 2018
  26. locked and limited conversation to collaborators on Jul 25, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    FixedA PR has been merged for this issueSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions