Typescript: Non-null assertion operator and the typeof operator

Created on 24 Jul 2017  路  24Comments  路  Source: microsoft/TypeScript

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.

Fixed Suggestion

Most helpful comment

Closing as fixed by NonNullable type.

All 24 comments

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>()

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.

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.

@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.

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.

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

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.

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

@bradzacher: I think you meant type level. :)

@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.

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.

@RyanCavanaugh is #6606 accepting PRs even in expression-based syntax?

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

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'.

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

@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)!

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

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

@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.

This should be doable today with NonNullable type.

@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?

do you know when that is being released?

TypeScript 2.8 should be available by the end of March.

Closing as fixed by NonNullable type.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tenry92 picture tenry92  路  146Comments

rwyborn picture rwyborn  路  210Comments

sandersn picture sandersn  路  265Comments

chanon picture chanon  路  138Comments

blakeembrey picture blakeembrey  路  171Comments