tc39 / proposal-nullish-coalescing

Nullish coalescing proposal x ?? y
https://tc39.github.io/proposal-nullish-coalescing/
1.23k stars 23 forks source link

Normative: Switch syntax to ??: #23

Closed littledan closed 5 years ago

littledan commented 6 years ago

Follows the conclusion of the discussion at https://github.com/tc39/proposal-optional-chaining/issues/34

littledan commented 6 years ago

Note the parallel change for optional chaining at https://github.com/tc39/proposal-optional-chaining/pull/48 .

hax commented 6 years ago

Too many ?... and ??. looks too close to ??: which very harmful to readability...

const city = staff??.company??.address??.city??:defaultCity;

PS. Compare to my syntax suggestion:

const city = staff!.company!.address!.country??defaultCity;

ljharb commented 6 years ago

Using !. suggests to me either a boolean operation, or conflicts with Ruby/Rails idioms meaning “mutates” or “throws”; separately, as I’ve already pointed out, ![ and !( conflict with existing meanings.

hax commented 6 years ago

@ljharb As I said before, TS already use ! as non-null assertion, a!.b, a![b], a!() are all valid in TS which compatible with ES2017, so I have no idea how "![ and !( conflict with existing meanings".

ljharb commented 6 years ago

@hax existing meanings in JS, and other languages. That TS chose that meaning does not mean it's idiomatic for JS, nor that it would be consistent with non-JS non-TS languages.

Separately, "assertion" usually means "throws if it fails" - which is the opposite of with optional chaining does.

littledan commented 6 years ago

@ljharb If this is a widely used feature in TypeScript, it could be a real problem for adoption here, which we could avoid with the strategy in this PR. This isn't the only language community where grammar intersection with different languages comes up; for example, C++ has to worry about Objective-C grammar when considering its evolution.

ljharb commented 6 years ago

@littledan makes complete sense; i'm mostly trying to argue that !. isn't a good choice regardless, such that I think its conflict with TypeScript can be made irrelevant.

hax commented 6 years ago

@ljharb If the original proposed a?.b can be seen as a == null ? null : a.b and not "conflict", then a!.b also can be seen as a != null ? a.b : null which not "conflict".

And if you not consider TS idioms, why consider Ruby/Rails or any other languages which more far from JS?

If this is a widely used feature in TypeScript, it could be a real problem for adoption here

@littledan See https://github.com/tc39/proposal-optional-chaining/issues/34#issuecomment-350641176

ScottRudiger commented 6 years ago

@hax @littledan @ljharb

And if you not consider TS idioms, why consider Ruby/Rails or any other languages which more far from JS?

If this is a widely used feature in TypeScript, it could be a real problem for adoption here

From the (hypothetical) perspective of someone who's learning JavaScript after coming from another language, I would think that

Ruby/Rails --> JavaScript is more common than TypeScript --> JavaScript

In the case of TypeScript, I would think most find themselves following the path: JavaScript (fundamentals, at least) --> TypeScript

so there is a kind of difference, in my opinion.

Further, (if perhaps more controversial)TypeScript is a superset of JavaScript so in the case of the two doubly-implementing the same feature (but with different syntax) I would think it's more logical for TypeScript to eventually deprecate said feature in a breaking update.

littledan commented 6 years ago

I'm not sure if it's feasible for TypeScript to make breaking updates these days. cc @rbuckton @DanielRosenwasser

hax commented 6 years ago

@ScottRudiger
I see it in another way. In my opinion, we should consider language idioms in this order:

  1. JavaScript itself
  2. TypeScript, ActionScript, flow, or any JavaScript dialects or extensions
  3. CoffeeScript, LiveScript, or any languages mainly based on JS semantics and set JS as the first compiling target
  4. Python, C#, Groovy, Swift, PHP, Ruby... and any other languages

IMO, it begin at the wrong end to consider the new comers from Ruby/Rails prior to the core users from TS. NOTE: all TS users are actually JS heavy users!

DanielRosenwasser commented 6 years ago

To start off, I'd like to clarify - TypeScript's postfix ! semantics actually differ enough in this respect that it would be a problem. It doesn't correspond to a "carry over undefined (and/or null) if the left side of the ! is null/undefined". In fact, it outright removes the null/undefined from the type of the LHS.

But apart from that, I have to agree that I don't feel the ! conveys enough about the optional chaining. I'm not so hot on ?? either, but I'll have to discuss with others on my team.

hax commented 6 years ago

@DanielRosenwasser @littledan About repurpose!, you could check my original post: https://github.com/tc39/proposal-optional-chaining/issues/34#issuecomment-350641176

To recap: change non-null assertion to !!(same as Kotlin) and use ! for non-null chaining is not a "very breaking" change for TS.

Anyway, in this thread I just want to argue the problem of differentiating ??. and ??: . I only use !. as a comparison, but other alternatives also apply. Let's continue discussion in https://github.com/tc39/proposal-optional-chaining/issues/34 .

DanielRosenwasser commented 6 years ago

is not a "very breaking" change for TS.

It's definitely a breaking change. :smile: For example, changing the semantics to null-propagation means that you could absolutely introduce a type error under in the expression a!.b.c which could break a user's build.

Just to clarify, consider the following:

declare let a: null | {
  b: {
    c: number
  }
};

// Non-null assertion: has type `number` 
a!.b.c;

// Rough equivalent of the null-propagation `a?.b.c`; is a type error
(a != null ? a.b : a).c;
hax commented 6 years ago

@DanielRosenwasser Yes it will introduce compile-time type errors if strict null check is enabled (I already point out this in the original post). But by default ( noEmitOnError default to false) TS compiler will still emit the code, and the runtime result is same -- if a.b.c (current compile output) works then (a != null ? a.b : a).c will also work -- because T is the subtype of T | null | undefined.

That's what I mean "not very breaking". (Forgive my poor english if I express the wrong meaning)

And, it's very easy to migrate old ! to !!, you even can do it now, because !! is valid and have the same result now. So I can imagine if ! proposal was accepted, it's very easy to provide a migration tool in next TS release :-)

0rvar commented 5 years ago

I do not like these changes. Can someone summarise the problem with the current syntax?

claudepache commented 5 years ago

@0rvar This change is required if https://github.com/tc39/proposal-optional-chaining/pull/48 is adopted, because otherwise a??[b] would become ambiguous. (But at this point, there is not much chance that https://github.com/tc39/proposal-optional-chaining/pull/48 will be adopted, and therefore neither the present PR.)

littledan commented 5 years ago

Closing per https://github.com/tc39/proposal-nullish-coalescing/pull/23#issuecomment-478002524