Typescript: 建议:“安全导航算子”,即x?.y

创建于 2014-07-15  ·  205评论  ·  资料来源: microsoft/TypeScript

当前状态

  • TC39 提案现在处于第 3 阶段 (🎉🎉🎉🎉🎉)
  • 实施正在进行中
  • 你可以在 TypeScript 3.7 中期待这个特性
  • 当它在夜间版本中可用时,我们将在此处更新
  • 推迟可选调用,直到委员会澄清其语义

开放式问题

  • document.all应该得到什么特殊情况(如果有的话)?

C# 和其他语言具有访问属性链的语法糖,其中null (或在我们的例子中, undefined )可能在对象层次结构中的任何点遇到。

var x = { y: { z: null, q: undefined } };
console.log(x?.y?.z?.foo); // Should print 'null'
console.log(x?.baz); // Still an error
console.log(x.y.q?.bar); // Should print 'undefined'

需要关于我们应该编码什么的建议,记住访问器的副作用。


@DanielRosenwasser编辑,2018 年 2 月 27 日:该提案也称为“空传播”运算符。

Committed ES Next Suggestion Update Docs on Next Release

最有用的评论

可选链接是第 3 阶段

出于庆祝目的简要解锁此

所有205条评论

所以在第一个例子中,我们可能会像下面这样发出它:

x && xy && xyz && xyzfoo

但是我们必须以某种方式使 x、y、z 和 foo 每个都最多计算一次。

在许多情况下,你也真的不能做&& ,因为真实性对原语来说有点问题。

例如:

"     "?.trim()?.indexOf("hello")

给出""

因此,对于一般情况,您需要使用==null进行一些明确的比较,除非我们利用类型系统(看到我们这样做会很酷)。

我们可能会发出一个 monadic-bind 函数(对于 JS 输出来说不是很漂亮),或者对三元运算符使用一些转换(更接近典型的 JS 等效项)。 我显然有点偏向后者。

:+1:

理想情况下,我们应该让 ES7(或 ES8 或 ES9 或 ...)首先实现它,因为对于是否实际使用0 / ""的确切语义可能存在一些分歧

:+1: 我希望 TypeScript 能先得到它,而不必等待 ESxx。

事实上,像“?”这样简单且非常有用的空安全运算符。 和“?:”不在 ES6 规范中意味着将 ES6 规范放在一起的人应该羞愧地低着头。 这是一件如此简单而明显的事情,坦率地说,如果不将它纳入其中,那就太疯狂了。 大多数现代语言支持这些是有原因的:它们是不可或缺的。

我意识到这将偏离当前的规范(因为当前的规范是如此短视以至于忽略了这一点)。 但它非常有用,我认为这种单一的偏差是合理的。 如果最终将其添加到 ES 规范中,绝大多数(VAST)的 TS 开发人员不会受到实现的微小更改的影响。 这将提供的巨大好处值得对一小部分开发人员产生潜在的未来影响。 考虑到 ES 规范过程慢得可笑,这在几年内(至少)都无关紧要。

我完全同意brain428

@brian428这里的问题是该运算符可能在 ES7 中实现,因此如果 typescript 使用的规范最终与最终的 ES7 不同,那么没有人会高兴。

这里的问题是该运算符可能在 ES7 中实现,因此如果 typescript 使用的规范最终与最终的 ES7 不同,那么没有人会高兴。

我认为TypeScript实现可能(或可能不会)进入未来 ES 版本的功能是一种更积极的方法,因为它将成为影响 ES 方向的有用测试平台。

这是一个受 TypeScript 影响的 ES 讨论示例:

通过构造函数参数之一上的私有前缀声明和初始化的 TypeScript... 选项将对许多开发人员有所帮助

此外,ES 肯定有可能采用 TypeScript 中已经存在的功能,但具有不同的语义(例如,围绕模块的工作方式)。

ES 肯定有可能采用 TypeScript 中已经存在但语义不同的特性

我应该指出,我们普遍认为这是最坏的情况。 我们真的希望 ES6 中的模块在我们宣布 TypeScript 1.0 之前完成,但委员会的日程延迟阻止了这一点。 这是要避免的,不要重复。 我们真的很想找到那些有大约 0% 的机会进入 ES7+ 的功能(例如类型注释),或者有大约 100% 的机会通过易于定义的语义(例如,胖箭头是两个几年前)。 新的运营商很可能陷入尴尬的中间。

在最坏的情况下,如果 ES7 确实不同,编译器标志是否可以支持旧的 TS 实现,从而提供宽限期? 再加上清晰的迁移文档,应该为开发人员提供通往任何新标准的直接途径。

最终,使用任何此类功能(尽管非常有用)对开发人员来说都不是必需的。 TS 应该从第一天起就清楚地说明其使用对未来的潜在影响。 不喜欢潜在的托管重构的想法,不要使用它。 也许是一个可选的编译器标志来强制执行此消息?

TS不应该因为想要影响ES而疯狂,但是在这种孤立的小案例中,如果TS完全回避,那就太可惜了。

也许我们可以为此提出一个稻草人提案,然后在 --harmony 标志(或类似的东西)后面有一个参考实现。 这样我们就可以推动这个特性的 ES7 开发。

为了防止由于重复查找而产生的副作用,编译器要么必须输出临时变量:

($tmp0 = x, $tmp0 === void 0 ? void 0 : 
    ($tmp1=$tmp0.y,  $tmp1 === void 0 ? void 0 : 
        ($tmp2 = $tmp1.z,  $tmp2 === void 0 ? void 0 : $tmp2)))

或使用基于Proxy的记忆膜。

从分类的角度来看,这只是应用于属性查找的可能 monad,因此对于所有属性查找都可能返回未定义的语言来说,这是一个非常自然的功能。 如果 ES7 采用了上述代码所描述的语义之外的任何语义,我会感到惊讶。

codeplex 问题获得了相当多的票数(61)

我真的非常需要这个来减轻使用atomatom-typescript的痛苦。

它在 coffescript 代码中非常惯用(尽管我不希望它像确定论那样流行,而不是软弱无力的? )。 打开任何 coffescript 文件,尤其是像 space-pen 一样直接与 DOM 一起工作的文件(其中函数可以在视图被销毁或附加视图之前运行),你会发现大量的?用法。 例如这个文件有16 https://github.com/atom-community/autocomplete-plus/blob/f17659ad4fecbd69855dfaf00c11856572ad26e7/lib/suggestion-list-element.coffee

再说一次,我不喜欢我需要这个,但它是 JavaScript 的状态,我宁愿?而不是一百万if( && fest ) { then }

但我真的真的需要它来保持我的代码可读性。 当您等待 XHR 完成并且 Angular 运行其摘要循环时,_need_ 这也很常见。

好的,现在我已经阅读了该主题,看看我们为什么要等待。 我理解_sigh_。

we'd have to somehow make x, y, z, and foo each evaluate at most once.

coffeescript 确实做了一些优化,例如存储中间访问结果

typeof foo !== "undefined" && foo !== null ? (ref = foo.bar) != null ? ref.baz() : void 0 : void 0;

(我强烈认为打字稿不需要undefined检查:因为我们应该有一个由打字稿检查的var init)

+1

在今天的新闻中,Dart 正在获得官方支持: https ://github.com/gbracha/nullAwareOperators/blob/master/proposal.md

非常重要的功能。

可能是一个现成的想法,但如果每个人都认为可以使用键属性访问来处理该功能,则该功能的代码生成可以非常容易地完成而不会产生副作用:

if (aaa?.bbb?.ccc) {}

可以编译为

if (__chain(aaa, "bbb", "ccc")) {}

必须发出类似于__extends__chain函数。 __chain函数可以遍历arguments数组,当即将到来的成员不是instanceof Object或不包含成员名称时返回null 。 函数调用可以通过传入一个数组作为参数来处理(并在幕后使用.apply() ),所以......

if (aaa?.bbb?.ccc?(1, 2, 3)) {}

可以编译为

if (__chain(aaa, "bbb", "ccc", [1, 2, 3])) {}

即使对于长链,这也将使生成的 JS 保持合理的惯用语。

显然需要改进......但也许这里有什么东西?

如果aaa?.bbb?.ccc?返回a.b.c的值,如果所有的 props 都存在并且它恰好是一个函数,那么就不能

if (aaa?.bbb?.ccc?(1, 2, 3)) {}

编译为

if (__chain(aaa, "bbb", "ccc")(1, 2, 3)) {}

?

@metaweta :您的解决方案仅检查void 0 ...不是那种破坏功能的目的吗?

那这个呢:

var result = one?.two?.three;

生成:

var $a, $b, $c;
var result = $a = one, $b = $a ? $a.two : void 0, $b ? $b.three : void 0;

我很确定这可以处理所有情况。 函数调用可能需要instanceof Function检查。

(这里会发出意外的局部变量的小缺点......可能会被包装在 IIFE 中)

@kevinb7 :如果ccc不是函数会怎样? 使用您描述的代码, __chain将始终必须返回有效函数,否则会发出 TypeError 。

@Back-io 当属性不存在时,查找返回 undefined === void 0。您的解决方案无法查找空字符串和零等虚假值的属性。

@metaweta :不,它没有:http: //jsfiddle.net/25LppbL6/

另外,我想 TS 团队并不热衷于使用松散的平等,因为有些人的 linter 警告过它。

@Back-io 是的:http: //jsfiddle.net/25LppbL6/2/

@Back-io 关于null,我不确定 a?.b 的预期语义是什么。 如果它是“如果定义了属性 b 然后使用它”,那么我的代码几乎是正确的。 获得 null 的唯一方法是将其分配为 null,因为查找不存在的属性返回未定义。 它确实无法捕获属性存在但设置为未定义的情况。 为了完全正确,它将检查 Object.hasOwnProperty() 而不是与 void 0 === undefined 进行比较。

如果语义是“如果属性 b 是真实的,则使用它”,那么您的代码很好,并且在某种程度上符合 JS 习语。

嗯......除非我遗漏了什么......你对小提琴所做的改变只是进一步证明我是对的......在所有 3 种情况下, var result仍然是undefined 。 而且我不确定您要通过扩展原始原型来提出什么情况...

我相当肯定该功能的行为是用void 0短路而不是产生错误。 (我喜欢您上面的想法,即在缺少成员的情况下它应该始终返回 void 0)。

''?.toString的预期输出是什么?

@kevinb7 :如果 ccc 不是函数会发生什么? 使用您描述的代码, __chain 将始终必须返回有效函数,否则会发出 TypeError 。

好点子。

@metaweta :我想大多数人会期望引用toString函数,所以我明白你的观点在哪里,在你访问 0 的原型成员的情况下它确实有点崩溃,假的,要么 ''。

:+1:
Angular 2 将 Elvis Operator 添加到他们的模板语法中

@metaweta

''?.toString 的预期输出是什么?

如果您的意思是''?.toString()它将是:

if ('' != null) {
  ''.toString();
}

样本

也许我们可以为此提出一个稻草人提案

@kevinb7它已经存在: http ://wiki.ecmascript.org/doku.php?id=strawman :existential_operator

我做了一些快速测试以在PropertyAccessExpression之上实现它作为它的特殊情况,但效果不佳,因为我们确实需要?.是右关联的(而不是像.这样的左关联

BrendenEich评论也反映了这一点。

我做了一些快速测试以在 PropertyAccessExpression 之上实现它作为它的特殊情况,但没有像我们真正需要的那样工作得很好? 是右结合的(而不是像 . 那样的左结合),否则发射器变得不必要的复杂。

@basarat你能详细说明一下吗? 右关联和左关联?.之间差异的示例对我非常有帮助。

@zlumer

右联想和左联想之间区别的一个例子? 对我很有帮助。

.保留关联,因此表达式foo?.bar?.baz变为 AST(如果我们将?.视为相同):

                    // foo.bar.baz = PropertyAccessExpression
                    //   .expr foo.bar =  PropertyAccessExpression
                    //     .expr foo = Identifier
                    //     .name bar = Identifier
                    //   .name baz = Identifier

所需的 JavaScript 发射是

foo != null ? (ref_1 = foo.bar) != null ? ref_1.baz() : void 0 : void 0;

如果我们在 AST 中有以下内容,则更容易执行此操作(尤其是 _recursively_):

                    // foo.bar.baz = PropertySafeAccessExpression
                    //   .name foo =  Identifier
                    //   .expr bar.baz = PropertySafeAccessExpression
                    //      .expr bar = Identifier
                    //      .name baz = Identifier

想想_如何将第一个 AST 转换为 JavaScript_,复杂性会更清楚。 希望这会有所帮助:玫瑰:

@zlumer ,为了补充@basarat所说的内容,我将发布示例1 + 2 + 3

解析时,我们必须决定先发生这些操作中的哪一个。

如果+是左结合的,这将被解释为((1 + 2) + 3)

如果+是右结合的,这将被解释为(1 + (2 + 3))

你可能会质疑这是否真的会有所作为。 在 JavaScript 中它会! 考虑示例"hello" + 2 + 3

  • 左结合: (("hello" + 2) + 3) => ("hello2" + 3) => "hello23"
  • 右结合: ("hello" + (2 + 3)) => ("hello" + 5) => "hello5"

(作为记录,JavaScript/TypeScript 对+运算符使用左关联性。)

@basarat ,我对@BrendanEich所说的话的理解(如果我错了,他可以纠正我 - 对 ping 感到抱歉!)_not_ ?.是右关联的,但 CoffeeScript 特殊情况下的属性访问?.的右边是右结合的。 例如,它将解析

o.p?.q.r.s

作为

((o . p) ?. (q . (r . s))) # or something close to this

代替

((((o . p) ?. q) . r) . s)

因为它更容易发射。

我们的 AST 需要很好地进行合理的语义分析,因此我们的发射过程可以稍微复杂一些来满足这种需求。

@basarat @DanielRosenwasser谢谢你的解释。 到目前为止,我了解它,但我仍然不确定一件事。
左关联?.非常明显且符合预期:

foo?.bar?.baz

变成(大约):

var ref = ((ref = foo) == null) ? null : ((ref = ref.bar) == null) ? null : ref.baz;

但我根本不明白右关联?.将如何工作。 你能举个例子吗?

但我根本不明白右关联如何? 工作。 你能举个例子吗

@zlumer运行时行为将保持关联。 我只是在谈论 AST,正如 DanielRosenwasser 也澄清的那样: is not that ?. is right-associative, but that CoffeeScript special cases property accesses on the right of the ?. to be right-associative

我们的 AST 需要很好地进行合理的语义分析,因此我们的发射过程可以稍微复杂一些来满足这种需求。

@DanielRosenwasser感谢您的反馈:玫瑰:

@basarat谢谢,突然间一切都变得清晰了:笑脸:

类似于@basarat 2 月的评论,我.... _sigh_...

但是,如果您考虑一下,99% 的用例将是检查指向对象的空指针。 坦率地说,当xnumber $ 时,谁会做x?.b?.c #$ ? 当我们_不_谈论一个对象时,_long_链在现实生活中的用例并不多(可能的例外是string )。 对于短链,我认为我们可以使用x && x.bx === 0 ? null : x.b

那么,我们可以说?.仅适用于对象类型吗? 任何其他类型都会引发语法错误。 并禁止在链侧调用函数。

然后整个事情转录为a && a.b && a.b.c

@schungx如果链中的成员是“任何”类型怎么办? 完全禁止? 或者只是让它通过并希望最好?

嗯,我的建议? 完全禁止。 太不优雅了,我知道... :-)

但我的理由是:

  1. 这是一个速记,所以如果有人使用any ,请使用长记。
  2. 如果有人在使用 TypeScript,很可能他/她正在使用它来支持打字,所以希望他/她不会有很多any
  3. any真的应该小心处理。 允许使用像any这样的灵活类型这样的简写确实是在要求发生错误。 在我看来, any应该尽可能有限。 这有点像 C 语言的(void *) —— 你拿到了核弹这一事实并不意味着你必须仅仅因为你可以触发它!

这将是一个了不起的运营商! 特别是对于ES6 / ES7 / TypeScript

var error = a.b.c.d; //this would fail with error if a, b or c are null or undefined.
var current = a && a.b && a.b.c && a.b.c.d; // the current messy way to handle this
var currentBrackets = a && a['b'] && a['b']['c'] && a['b']['c']['d']; //the current messy way to handle this
var typeScript = a?.b?.c?.d; // The typescript way of handling the above mess with no errors
var typeScriptBrackets = a?['b']?['c']?['d']; //The typescript of handling the above mess with no errors

但是我提出一个更明确的建议——不要混淆? 从一个? b : c 语句与 a?.b 语句:

var doubleDots = a..b..c..d; //this would be ideal to understand that you assume that if any of a, b, c is null or undefined the result will be null or undefined.
var doubleDotsWithBrackets = a..['b']..['c']..['d'];

对于括号符号,我建议使用两个点而不是一个点,因为当使用非括号时,它与其他点一致。 因此,只有属性名称是静态的或通过括号动态的。

两个点,表示如果它的 null 或 undefined 停止进一步处理并假设表达式的结果是 null 或 undefined。 (因为 d 将为空或未定义)。

两个点使它更清晰、更明显、更立体,让您了解正在发生的事情。

这也不会弄乱数字-情况不一样,例如

1..toString(); // works returning '1'
var x = {};
x.1 = {y: 'test' }; //fails currently
x[1] = {y: 'test' }; //works currently 
var current = x[1].y; //works
var missing= x[2].y; //throws exception
var assume= x && x[2] && x[2].y; // works but very messy

关于数字两个选项:您的电话可以采用哪个,但我建议第一个与现有规则兼容!

  1. 应该像现在一样失败( x.1.y == runtime error
var err = x..1..y; // should fail as well, since 1 is not a good property name, nor a number to call a method, since it's after x object.
  1. 应该可以工作,因为它知道这不是从Number.prototype调用属性的数字
var err = x..1..y; // should work as well, resulting 'test' in this case
var err = x..2..y; // should work as well, resulting undefined in this case

使用动态名称:

var correct1 = x..[1]..y; //would work returning 'test'
var correct2 = x..[2]..y; //would work returning undefined;

各位怎么看?

PS foo?.barfoo?['bar']语法也可以。

然而,同时使用当前的? :运算符和?.在同一行上可能会非常混乱。

例如使用?.?['prop']

var a = { x: { y: 1 } };
var b = condition ? a?.x.?y : a?.y?.z;
var c = condition ? a?['x']?['y'] : a?['y']?['z'];

而不是双点....['prop']

var a = { x: { y: 1 } };
var b = condition ? a..x..y : a..y..z;
var c = condition ? a..['x']..['y'] : a..['y']..['z'];
你觉得哪一个更清楚?

很有意思。 :+1:

恕我直言,两个点会更加混乱。 有些语言中两个点代表一个范围(例如1..4 ),TypeScript 将来可能会添加此功能。

问号还具有不确定性或条件性的语义含义,两个点表示的含义不同。

@schungx 很公平,但它有助于解决这样的奇怪可能性: a?['b']a?()

+1 ?

a?['b']a?()在 coffeescript 中的表现很好,我觉得它们很好看。

但话又说回来,我可能只是对咖啡脚本视而不见。

+1 Ruby 刚刚实现了存在运算符https://twitter.com/mikepack_/status/657229703443451904。 不管具体的语法如何,Typescript 也需要这个。

这将使代码更干净👍

+1 需要这个!

请,实施? 运算符,就像 C# 一样

+1 我已经错过了很长时间

+1
写得太难看了: someVariable && someVariable.someMember
什么时候可以写: someVariable?.someMember

+1,这会很棒....(我最想要的功能)

+1,这是个好主意!
只是,如果对象很复杂,表达式会充满?。 在每个属性(var result=myValue?.a?.b?.c?.d?.e;)之后,当我需要接收最后一个属性的值时(var result=?myValue.abcde;)。

+1 - 这可以说是 CoffeeScript 最伟大的特性之一,也是迄今为止我的团队在将我们的大部分代码从 CS 转换为 TS 之后最想要的 TypeScript 特性。

+1但是,这太复杂了:

var x = { y: { z: null, q: undefined } };
var z: x|y|z = x?.y?.z;

我喜欢这个:

var x = { y: { z: null, q: undefined } };
var z: z|void = x?.y?.z;

x?.y?.z的类型始终是z字段的类型。 当然类型必须是可空的,实际值可能是空的。 如果它是非空的,那么它必须是z字段的类型。

+1 这将与 Typescript 简化大规模复杂 JS 项目开发的愿景相吻合。

对此有何更新? 这是社区投票支持此功能的案例吗? 还是已经考虑过但存在一些工程挑战?

到目前为止没有更新,因为如果没有 ECMAScript 委员会的某种提议,引入新的表达式级语法是危险的。

请参阅https://github.com/Microsoft/TypeScript/issues/16#issuecomment -57645069。

这是关于存在运算符的最新讨论(很多想法,但看起来并不像很多行动):

我应该在哪里写“+1”以帮助将其带到 ES?

@msklvsk ESDiscuss

暂时关闭这个。 由于实际上没有任何特定于 TypeScript 的内容需要在表达式级别进行此操作,因此这种大的运算符更改应该发生在 ES 规范委员会而不是这里。

重新评估这一点的一般障碍将是到达下一阶段的具体 ES 提案,或者 ES 委员会的普遍共识,即该功能不会在很长一段时间内发生(以便我们可以定义自己的语义并合理地确保他们会“赢”)。

生活糟透了

删除独立的:+1:s。 请使用 GitHub 反应功能或向离您最近的 TC39 代表发送鲜花和糖果。

一旦我在 coffeescript 和 Swift 中习惯了它,就没有回头路了。 就目前而言,TS 对我来说已经死了。

@algesten如果我们考虑语言本身,我可以同意swift ,不确定长期coffescript支持(我在生产项目中使用 CoffeScript)。 我们可能同意确定 2016 年 6 月的语言分析趋势,我们可以清楚地看到最近coffescript的情况,而TypeScript在过去几年中增长最快:

TypeScript:在 Go 或 Swift 之外,我们近年来观察到的增长最快的语言是 TypeScript。 微软支持的 JavaScript 超集和 Angular 2 基金会连续第二个季度取得显着增长,从第 31 位跃升至第 26 位。 这是所有前 30 种语言中最大的单一变化,也是整体第二大跳跃(标准 ML,7 位)。 实际上,TypeScript 排在第 26 位,与 Erlang 并列,落后 Powershell 1 位,落后 CoffeeScript 4 位,排在前 20 名之外。该语言面临的问题不是它是否能成长,而是它是否有动力在接下来的两到三个季度内进入前 20 名,在此过程中超越 CoffeeScript 和 Lua 之类的公司。

直到 2014 年, coffescript的趋势都非常积极,正如您在这里看到的那样,那是下降开始的时候。

使用新的 typescript 2.0 严格的 null(未定义)检查,这是必须的,因为否则你不能使用函数链接!

这应该在 ECMAScript 的下一个版本中。 考虑到这在 vanilla JavaScript 中比 TypeScript 更有用,并且考虑到实现自然是对 undefined 或 null 的检查,而不是真实的检查,添加 TC39 应该是微不足道的。

简单的实现,也是 JavaScript 中惯用的实现,只是在遇到 null 或 undefined 时短路,并返回 undefined。

@bterlson这有意义吗? 这样的提议被接受的几率有多大?

打字稿的一大好处就是“今天给你未来”。 这是令我感到惊讶但尚不存在的功能之一。

我希望它会尽快添加,因为函数链接是流行的习惯用法,并且严格的空检查将不再有效,除非?。 添加了运算符。

TS 2 精彩的 null 检查使它成为必须拥有的好东西!

通读这个线程,我有点惊讶这没有得到维护者的更多关注。

在一个理想的世界里,TypeScript 应该引领 ES 而不是相反。 说真的,如果 TS 团队总是等待 ESx 提出并最​​终确定一个特性或语法,那么 TypeScript 现在会在哪里呢?

正如其他人指出的那样,这种语法确实是“必须具备的”。 到目前为止,它甚至在这个线程中收到了一些很好的建议。 我认为实施应该符合这些期望。

  • 通常,当且仅当a.b有效时,表达式a?.b应该在编译时有效。
  • 它应该只评估链中的每个表达式一次。
  • 应该是短路的。
  • 如果执行到达具有nullundefined值的中间表达式,则该值应该是返回值。

当 ES 指定时,您认为哪些部分会导致分歧?

对于a?.b ,我看不到与现有(和未来?)语法有任何冲突。 如果解析器找到标记?.它可以将其视为“安全导航运算符”(具有@cervengoc 描述的期望)。

仅当允许a?(b)a?[b]时才会出现语法冲突,因为它们也可以解释为三元?:运算符表达式的开始。 但对于初学者来说,我认为这些可以放在一边,只支持a?.b语法已经让很多开发人员高兴了!

在一个理想的世界里,TypeScript 应该引领 ES 而不是相反。

在表达式级别的语法方面,我们绝对不同意。 有一个委员会制定标准是有原因的——不是为了让一个玩家的一次性行为可以单方面决定 JavaScript 的未来。

正如其他人指出的那样,这种语法确实是“必须具备的”。

如果这是 TypeScript 的必备品,那么它也是 JavaScript 的必备品! 再次,将您的疑虑提交给 ECMAScript 委员会

当 ES 指定时,您认为哪些部分会导致分歧?

至少,我认为在遇到这些值时语法是否会短路到nullundefined或者总是短路到undefined会有分歧。 关于是否支持某种形式的括号语法也会存在一些争论。 还有一个问题是a?.b.c的行为是什么。 还有?. vs .? vs a.b?的问题。 问题是这对delete运算符的影响是什么。

ES 讨论线程对此有超过 100 条评论。 不乏歧义! 很容易孤立地看一个例子,并认为不可能有大量的极端案例。 有。 这可能就是为什么在 TC39 上还没有人支持这一点,并且_也_为什么我们不急于添加一个在其行为方式上存在很多歧义的功能。

抱歉,我没有通读所提到的线程,我一定会看一下它以了解更多信息。

我们对此的看法有所不同。 关于委员会,老实说,这是 JavaScript 永远不会成为 _good_ 的最大原因之一。 举个例子,我见过的许多最成功的软件(如 Total Commander 或 IrfanView)之所以成功,是因为它们是由一个人维护和设计的,而不是由一个*委员会“。当然这不是一个完全正确的例子。但我几乎可以肯定,如果你一个人设计了完整的 ES6,那么现在世界会变得更美好。

此外,您提到的模棱两可在 99% 的 _theoretical_ 事物中都存在,并且与开发人员方面无关。 谁会关心它返回什么, nullundefined ? 随便挑一个,我们就这样使用它。

总而言之,你和那个委员会与我们大多数人站在不同的一边,而那一边的事情通常比实际情况更复杂。 至少可以说,这可能会导致一些反生产力。 不要个人认为,但根据我的总体经验,有些人最好经常走出会议室,看看一些代码。

当然没有冒犯,不要私信,我非常尊重你和所有的 TS 团队,因为你确实彻底改变了包括我在内的许多开发者的客户端开发,感谢你所做的所有工作。 我猜我们对这个特定的人有点失望。

最后一个想法。 我已经浏览了提到的 ES 线程,并且完全可以肯定一件事:它们过于复杂了。 他们想要设计适合各种场景的东西。

我个人会对有条件的成员访问运算符完全满意和满意。 不需要条件调用,不需要条件索引签名,不需要支持所有花哨的场景,只要是有效的 JS 代码。 就这样。 但相反,他们可能会继续坐在那里讨论如何一次完成所有事情,这是一个很棒的计划,但我们最终将一无所获。

总而言之,你和那个委员会与我们大多数人站在不同的一边,而那一边的事情通常比实际情况更复杂。

我认为您没有准确反映 Ryan 或 TC39 的真实状态。 Ryan 和 TypeScript 团队为 TypeScript 制定了非常明确的设计目标。 TypeScript 是 JavaScript 的超集。 不是人们想要的语言(例如 Dart、Haxe)。 在语法方面,TypeScript 团队已经学会了预先发明它(例如模块)的成本。 我们也一头扎进了类私有成员的挑战,其中提议的 ES 语法与 TypeScript 使用的语法完全不兼容。 为什么? 因为考虑到语言的运行时挑战,表面上看起来并不复杂的事情是不可能实现的。

在我看来,TC39 已经“保存”了 JavaScript。 ES4 被抛弃,不是因为它缺乏好的、创新的想法,而是因为它会破坏互联网。 TC39 形成了自己,他们在讨论中分享并完全开放,以及他们如何做出决定并为我们提供了 ES2015,它很像 ES4,但没有破坏互联网。 令人惊讶的是,我们基本上拥有运行 10 年前代码的 JavaScript 运行时,但支持对语言的许多重大改进。 ES2016 是暴风雨前的平静。 ES2017 具有“合理”数量的功能和变化,以及指向正确方向的清晰管理流程。

因此,在我看来,站在事物的“不同方面”显然是奏效的。 这胜过“必须具备”功能的权宜之计。

@kitsonk我并不是说“不同的一面”是负面的,尤其是我并不是要贬低 TypeScript 或 ES6 中的工作。 更重要的是,TypeScript IMO 中最好的一点是它确实有并且有一个明确的设计目标,并且它像许多其他开源东西一样受到很好的保护,不会成为破坏。

我只是想说,这个特性是一个明显的例子,说明一群天才最终会过度思考和过度复杂化事情,而不是仅仅走简单的方法,并接受一些限制,比如不支持调用或索引签名等. 那个论坛里有人甚至建议在作业中使用这种语法,这有点疯狂。 我仍然认为这种现象在这个意义上是适得其反的。

我知道在您这边,例如私有成员变得与最终的 ES6 概念不兼容是一件痛苦的事情。 但另一方面,我们拥有它。 早于 ES6。 这是我们这边的主要观点。 粗略地说,我们不关心你如何设法为它发出适当的代码,我们只是乐于使用它。 与模块相同,以及所有内容。 我们(或者至少我)没有看到你所说的那些痛苦,我们总是对私有成员或模块感到满意。

当我读到它时,这个特殊的功能在 CoffeScript 中。 为什么我们这些简单的开发人员在选择平台/库/插件等时总是要做出妥协? 我的意思是总是。 这有点烦人。 在这里,我们有一个伟大的语言,它具有巨大的潜力,它完全把所有其他参与者都抛在了后面(包括 ES!),它成功地彻底改变了客户端开发的很大一部分,当谈到这个“简单”的特性时(我的意思是至少成员访问部分),我们听说它不会在 ES 提交之前实现。

我想快速提醒大家,在今天的 TC39 会议上,此功能从第 0 阶段转移到第 1 阶段。

相关提交: https ://github.com/tc39/proposals/commit/cb447642290a55398d483f5b55fb7f973273c75d
会议议程: https ://github.com/tc39/agendas/blob/master/2017/01.md

哇! 那是巨大的!

我在这里看到了一些“惊喜”(并不是说我不同意,只是如果我们早点这样做,我们可能会做不同的事情):

  • null不是从a?.b表达式产生的:当anull时,这会产生undefined
  • ~ 链点传播:如果bc属性是undefined a?.b.c.d则不会抛出 a?.b.c.d ~ Ryan 看不懂
  • ~存在括号的传播:如果b未定义,即使(a?.b).c也不会抛出~ Ryan 无法阅读
  • ~传播甚至发生在方法调用上:如果c调用返回nulla?.b.c().d将返回undefined #$ ~ Ryan 无法阅读
  • 支持delete运算符
  • 支持括号语法a?.[x]
  • 支持函数调用语法func?.(...args) ,即使是非方法调用 (!)

从现在到第二阶段,我希望看到这些领域的变化。

我认为coffeescript 做对了。

如果 b 未定义,则 a?.bc 抛出。

a?() 和 a?[0] 都很好。

  • 链点中的传播:如果 b 和 c 属性未定义,a?.bcd 将不会抛出
  • 存在括号时的传播:如果 b 未定义,即使 (a?.b).c 也不会抛出
  • 传播甚至发生在方法调用上:如果 c 调用返回 null,a?.bc().d 将返回 undefined

这些观点对我来说似乎并不准确。 从提案中:

a?.b.c().d      // undefined if a is null/undefined, a.b.c().d otherwise.
                // NB: If a is not null/undefined, and a.b is nevertheless undefined,
                //     short-circuiting does *not* apply

哇,我完全看错了。 你说得对。 更新

@algesten来自原始提案:

a?.()

b?.[0]

甜的。 运算符可以有点像?.那么。

这里发生了一些额外的对话: https ://github.com/estree/estree/issues/146

一句很适用的名言:«让简单的事情变得简单,让困难的事情成为可能»。 因此,也许可以很好地支持最常见的情况,同时跳过(至少最初是)复杂/罕见的情况,同时仍然允许使用更长的(现有的)语法“手动完成”。

只是我的两分钱

let a = b?.c?.d?.e;

到:

let a;
try{
   a = b.c.d.e;
}catch(e){
   a = undefined;
}

@cedvdb完全不同的语义——getter 中抛出的异常不应导致合并

@RyanCavanaugh是的.. 我没有想到这一点。

这是现在实施的雷达,还是 TS 团队会等待 ES 提案进一步推进?

它在我们的短名单上; 我们仍有一些问题/疑虑,但您应该会在接下来的一个月中看到这方面的进展。

不确定@mhegazy的评论来自哪里——关于 TC39 提案的未决问题数量太大,我们无法在这里进行有意义的工作。 需要首先解决有关nullundefined如何交互以及实际支持哪种语法的具体问题。 第 2 阶段是绝对最低限度,考虑到对运行时行为的影响,我们更喜欢第 3 阶段。

这简单的代码有效吗?

a == undefined ? expression : undefined

expression表示 ax, a[x], a(x), delete也可以在这里生成

然后a?.b?.[c]?.(d)将生成

a == undefined ? (a.b == undefined ? (a.b[c] == undefined ? a.b[c](d) : undefined) : undefined) : undefined

似乎会通过所有 RyanCavanaugh 的规则


如果讨厌==运算符,它也可能是a === undefined || a === null

@zh99998你 _have_ 讨厌==因为''0也是等同的。 几乎有人认为,运行时行为应该是对(typeof value === 'object' || typeof value === 'function' || typeof value === 'symbol') && value !== null的某种检查,这现在变得相当复杂。

正如@RyanCavanaugh所说,在TC39 提案至少进展到第 2 或第 3 阶段之前,它不太可能取得进展。

我看到==只等于nullundefined
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness

并在 chrome 控制台中通过测试:

'' == undefined
false
0 == undefined
false

@kitsonk undefined 仅强制为 null ,反之亦然。 没有其他值强制为 undefined 或 null。

您似乎对虚假​​值感到困惑。 0 和 "" 确实是虚假的,但永远不会强制为 null 或 undefined。

==意味着强制。 null == 0是假的,因为除了 undefined 之外没有任何东西可以强制为空。 undefined == 0也是如此。

另一个例子可能是

    if(!NaN) console.log("NaN is falsy") // NaN is falsy
    if(false == NaN) console.log("NaN coerces to false")
   else console.log("NaN doesn't coerce to false");// NaN doesn't coerce

你得到图片。

我看到很多使用三元运算符的尝试,但是任何多次引用同一属性的实现都可能导致意外的副作用。

幸运的是,JavaScript 具有 IIFE,因此您可以将访问器的结果存储在函数参数中,尽可能多地引用它,并且永远不会多次评估它。 在下面的示例中,我创建了一个可以多次调用的coalesce lambda,以代替包含?.运算符的表达式。

语言实现者需要关心的一件事是表达式中间的数组和函数调用。 我认为编译器可以处理检测这些情况并将这些表达式简单地附加到合并调用的末尾。

const coalesce = (x: any, y: string) => x == null ? null : x[y];

const x = {
    y: {
        z: "Hello, World!!!"
    },
    f: () => "Foo!",
    a: ["Array!"]
};

// x?.y?.z
const value1 = coalesce(coalesce(x, 'y'), 'z');

// x?.f()
const value2 = coalesce(x, 'f')()

// x?.a[0]
const value3 = coalesce(x, 'a')[0]

请注意,不需要一个名为“coalesce”的实际全局。 该代码可以直接内联到任何表达式中。 不过,它可能会减少臃肿,给它一个名字。

我不太关心运算符使用的语法,或者语言实现者是否想要等待标准。 只是想我会炫耀另一种方法。

这也带来了对??运算符的需求。 在 C# 中,这会将任何null表达式替换为右侧的任何表达式。

string x = null ?? "Hello";
````

In JavaScript, it is more idiomatic to use `||` to replace "falsey" values with the value on the right. 

```javascript
var x = null || "Hello";

不幸的是,真实性捕获了太多的边缘情况( 0false等)。 使用空合并 ( ?. ) 运算符,您需要特定于null -ness 的东西。

const x = { y: "" };
const result1 = x?.y || "default";  // I'd expect "default"
const result2 = x?.y ?? "default";  // I'd expect "" 

@jehugaleahsa ,在您使用合并的示例中,如果之前的检查返回null,则必须有一种方法来防止发生函数调用和成员访问。 在 x?.f() 示例中,如果 x 为 null,则不应调用 f。

@bschlenk我不同意。 我认为它应该失败并显示类似null is not a function的消息,但这不取决于我,我猜。

最近大量评论详细说明了实现该运算符的可能方式,这有点奇怪。
实施可能是一个已解决的问题。

如果您有兴趣,这里有idx 库,它与?.运算符的行为非常相似,尽管它也忽略了计划运算符的许多细节。 无论如何,任何想知道如何实现这些东西的人都可能会对他们的编译输出规范感兴趣。

TS 可以输出类似的东西,也可以输出完全不同的东西,但我认为这不是我们在这里等待的。 这里已经说过很多次了,直到 ES 提案向一个方向或另一个方向移动,TS 才会得到运营商。

仍然有一些未知数的语义,在此处此处列出。

请放心,我们将正确实施这一点,并且不需要另外 100 条评论来弄清楚||? ... : ...的作用。 正如@noppa所说,我们只是在等待 ES 规范最终确定。

https://github.com/babel/babel/pull/5813 (伴随babylon PR )刚刚被合并到 Babel 的preset-stage-1中。 当然,该规范仍处于第 1 阶段,但这将有助于推动它向前发展。

也许我错了,但我在这个帖子中没有看到任何与 tc39 提案的明显链接,所以这里是为未来的读者准备的: https ://github.com/tc39/proposal-optional-chaining

仅供参考,可选链接将在下周的 TC39 https://github.com/tc39/agendas/blob/master/2017/07.md

@jehugaleahsa我认为你是对的,我将在下周的 TC39 上展示?? 。 您可以在此处遵循该提案: https ://github.com/gisenberg/proposal-nullary-coalescing

我看到 TC39 中涵盖了可选链接……结果是什么?
希望这足以在 Typescript 中推动这一进程😉

@markwhitfeld来自注释摘要
可选链接运算符:仍处于第 1 阶段,稍后将提供更清晰的各种选项定义和反馈答案

完整注释: https ://github.com/rwaldron/tc39-notes/blob/master/es8/2017-07/jul-27.md#13iia -optional-chaining-operator

关于操作符的行为方式仍然存在悬而未决的问题,因此它似乎还没有处于可以添加到 TypeScript 的状态。

从这些说明来看,委员会似乎不知道提出的选项是如何工作的,我原以为他们会在讨论之前了解他们计划讨论的提案。 我想在我可以启用严格的空检查之前还需要一段时间。

我非常希望他们采用选项 2/4(无论如何这是提案的当前状态)。

2014 年 7 月 15 日 - 2017 年 9 月 4 日,还没有

@frankfvb你显然没有读过这个问题。

有很多讨论导致核心团队认为,在ECMAScript 提案取得进一步进展之前,在这个关键时刻实施将是不谨慎的,这将直接影响 TypeScript 中此功能的功能。

在 ECMAScript 标准委员会的最后一次会议上,该提案仍停留在第 1 阶段,因为它对如何实施存在一些非常基本的问题。 虽然不是硬性规定,但 TypeScript 只会实施第 3 阶段的提案。 如果他们认为第 2 阶段提案至关重要并且在 TypeScript 中的潜在使用推动了标准的发展,它有时会实施第 2 阶段提案。

我不确定人们对此有多清楚。

正如我之前所说,这是我们的短名单。 我们正在等待 TC39 就运算符的语义达成某种共识。 我们不想把它放出来然后破坏用户。

这不是重新讨论 TC39 讨论的主题

如果您想在提案中权衡,请在适当的地方对其进行评论我也有意见,但将它们放在这个线程中不会有任何作用。

我创建了一些简单的东西来满足我当前的需求。 它仅适用于每个链接都是属性名称的链,因此不支持访问数组中的元素(例如)。

在 TypeScript 中实现一个非常简单的 Elvis 运算符

此外,如果您有 lodash/underscore,您已经可以使用_.get(Book, 'author.name.firstName')来满足您的需求

编辑:由于_.get()方法的类型问题,显然这是一个不好的建议。 请参阅下面的评论

@tolgaek_.get有错误的类型,即使有这种更好的类型(由于作者的原因,尚未合并),只有当对象深度为 1 时,打字稿才能绝对推断出结果类型,在所有其他情况下,它是any并且必须在运行时检查

另一方面,使用 elvis 运算符 typescript 可能能够推断出具有任何深度的对象的结果类型,这就是我期待 elvis 运算符的原因

哦,我明白了,我不知道有打字问题。 谢谢@BjornMelgaard

@mhegazy不能先实现此功能并将其标记为实验功能吗? 我认为如果实验功能的规格发生变化,人们应该不会有问题。

猫王不乐意等这么久。

它已登陆 Babel7。 打字稿,我们运行缓慢.. 有人请让这发生。
:)

@gs-akhan Babel 插件实现了几个月前提案的旧版本。 从那以后,提案发生了变化(包括对运算符的解析方式进行了重大更改),并且在功能达到阶段 2(更不用说阶段 3)之前可能会有更多变化,因此任何使用当前 babel 编写的代码发布实际功能时,插件可能会中断。 Babel 有意在提议的特性稳定之前实现它们,以便规范作者和其他感兴趣的人可以尝试提议的特性。 仅仅因为 Babel 已经实现了一个特性,并不意味着它可以在未来不需要重大更改的方式实现。

@alangpierce这是有道理的。 谢谢

我知道这是一个非常非常好的运营商,但在其规则被取消之前就可以使用它是一把枪,我们不会这样做。 算子的运行时行为仍在不断变化; 如果您今天编写代码,它明天可能会以不立即显现的方式停止工作 - 可能是罕见的崩溃,可能是数据损坏,谁知道呢? 现在稍有耐心将在几个月后为您节省很多痛苦。

开始认为这张票应该被锁定(而不是关闭),直到规范准备好。

规范什么时候准备好?

@oliverjanik当前的规范草案可以在这里找到。 在下一次 TC39 会议(9/26-9/28) 上,有一个议程项目将提案推进到第 2 阶段。 届时我将展示这些幻灯片。 对于希望尽早审查并提供反馈的人,请在提案回购中提出问题。

非常感谢@gisenberg为我们支持这个问题! 我正在考虑制作一个摘要幻灯片,以帮助理清可在 TC39 会议上使用的操作员选项,以避免混淆,但您已经这样做了。 很棒的工作!
也许对 TC39 对话(和幻灯片)有益的另一件事是查看其他语言中运算符的语义和语法。 尽管其他语言不一定规定它应该如何在 Javascript 中工作,但保持运算符与其他语言的运算符相似以避免混淆会很有帮助。
祝下周好运!!!

抱歉再次跑题了,但我想这里的一些人可能会发现有趣的是,在 Flow 中现在可以为_.get等安全 getter 函数添加一些工作类型定义。

示例: flowtype.org/try

这不是有史以来最漂亮的一段代码,它也不能正确区分 null 和 undefined,但除此之外,它似乎工作得很好。

AFAIK TS 中唯一缺少的东西就是$NonMaybeType之类的东西。
当然,并不是说它会消除对这个操作员的需求,我只是觉得这很酷。

由于担心括号、点、调用访问和语义行为的句法一致性( undefined / null右侧表达式的副作用的可预测性),这在最近的 TC39 会议上没有达到第 2 阶段x.bnumber x.?b()

(对此评论投票🎉扔烂水果)

感谢您让我们知道。 真可惜。 也许需要缩小范围以使其更简单但仍然有用?

也许需要缩小范围以使其更简单但仍然有用?

这是 TC39 面临的挑战,虽然很糟糕,但我不得不承认我很高兴人们正在经历这些。 他们很难引入相当复杂的语法,事实上,在这种情况下,语言的弱类型实际上导致了大量必须解决的边缘情况,或者你得到的代码是💥对任何人都不好。 我不认为如果你引入这样的运算符,你实际上可以缩小它的范围。 实现者更容易覆盖 90% 的情况,但我认为¯\_(ツ)_/¯不是剩余 10% 的有效代码。

伙计们,3 年多之后,我会说是时候对委员会说“操你了”,并按照我们自己的方式去做,不管 TypeScript 惯用什么。 无论如何,如果没有静态类型,此功能将无法正常工作。

使用与 TC39 命题明显不兼容的语法创建 TypeScript 实现。 一旦 ES 获得了安全导航运算符,TypeScript 将有两种选择:一种具有与 ES 兼容的糟糕语义,另一种与类型系统相关联。 这意味着您不能将 TS 安全导航运算符与任何“类型”一起使用,这很好。

@notsnotso Typescript 不应该破坏 JS,这就是 Coffeescript 的用途。

也许好的解决方案是:

  • 实现很简单? 不耐烦的开发人员的操作员,作为实验性功能(如装饰器),带有警告 - 将来会破坏您的代码
  • 再等3年,标准将被编写,作为非实验性功能实现。 写教程,有什么会坏的。 即使使用静态类型,当人们使用新的运算符实现编译代码时,也可以编写警告。
    “用我们自己的方式,不管 TypeScript 的惯用方式”不是一个案例,因为 3 年后人们将面临问题,即 ts elvis 的工作方式与 js 中的不同。

不过,老实说,我并不感到惊讶。 在我原来的
帖子中,我指出了关于索引、函数调用等的歧义。
老实说,我对?.的想法越多,感觉就越不像
属于 JavaScript,因为它有undefined

我宁愿一个更通用的实用程序来解决这个问题
和更多。 有人认为我有类似内联try/catch
表达式 ( let x = try a.b.c else 0 ) 与一个运算符结合使用
检查“nully”(例如,x ?? 1)而不是“falsey”(例如,x || 1)。
所以,你可以像这样组合它们: try a.b.c ?? 0 else 0 。 很啰嗦,是的,
但它基本上说:尝试评估 abc ,如果结果是null
undefined ,返回 0。如果 a 或 b 为undefined并抛出异常,
抓住它并返回一个 0。

另一种方法是使else子句可选,默认为
undefined 。 然后你可以把表达式写成: let x= (try a.b.c) ?? 0 。 这非常紧凑,避免了歧义并提供了更多
可以解决其他问题的通用解决方案。

我并不是说我们应该推动这一点。 我所说的一切都在那里
?.的替代品,我们应该真正探索我们的选择,直到我们
找到一个适合 JavaScript 语言的。

2017 年 10 月 5 日星期四上午 7:51,Dmitry Radkovskiy [email protected]
写道:

@notsnotso Typescript 不应该破坏 JS,就是这样
Coffeescript 是为。


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/Microsoft/TypeScript/issues/16#issuecomment-334441781
或使线程静音
https://github.com/notifications/unsubscribe-auth/ABTgPilbZfuKc2egdBrYfdTHHeDl3F6Sks5spMLLgaJpZM4CNapf
.

@zlumer开个玩笑。 也就是说,特定于 TS 的功能不会破坏 Javascript。

@jehugaleahsa我喜欢你的提议,并且可以在其他语言中找到它。 我认为它适用于 TS。

我会说我真的不明白等到 ECMAScript 接受运算符的强烈需求。 TypeScript 早在进入 ECMAScript 之前就添加了类、模块、lambda。 事实上,TypeScript 的既定目标之一是/正在试验实验性 JS 功能。 TypeScript 提供自己的实现无疑将有助于委员会的这些讨论。

在委员会最终转向不同的方向之前,你们一直愿意进行重大更改,我真的不明白为什么这个功能如此不同。

async/await 也很早就被引入到 TypeScript 中。

也就是说,特定于 TS 的功能不会破坏 Javascript。

不会有“TS 特定功能”之类的内容,请查看TypeScript 设计目标以获取更多信息。

TypeScript 在 ES 走向光明的道路之前到达,可能有一些现有的,它们只是为了兼容性而保留的。

TypeScript 早在进入 ECMAScript 之前就添加了类、模块、lambda。

尤其是模块是一个巨大的崩溃,仍然会导致混乱。 这是 TypeScript 团队反复使用的一个例子,作为一个_错误_和过早开枪的例子。 我们现在也有私人领域迫在眉睫,我再次感谢过去 5 年的private ,但它会造成无休止的混乱。

同样,装饰器已经在旗帜下可用,但现在装饰器实际上已经进入第 3 阶段,它将需要重新实现 TypeScript 并破坏代码。 这就是为什么这些东西_不能_被认为是可能的。

async/await 也很早就被引入到 TypeScript 中。

一旦 ECMAScript 提案达到第 3 阶段。

没有人行为不端,但如果这个对话只能绕圈子(如果有标志怎么办?是的,我们知道标志)或离题(如果 TS 不再是 JS 怎么办?不,五年后我们不会改变)我们的想法),我们不需要拥有它。 我们已经说过大约 3 年了,我们将在 ES 委员会锁定其语义时准确地实现这一点。

再一次,提案仓库是https://github.com/tc39/proposal-optional-chaining ,你可以在那里关注它的进展。 我们将离线工作以尝试提高提案在下一次 TC39 会议上的机会,因为我们真的希望这也能通过。

更新:我们今天下午在 TC39 达到了第 2 阶段!!!

可选链接是第 3 阶段

出于庆祝目的简要解锁此

万岁!

不要垃圾邮件...

您可以发送表情符号表达您的感受

不要垃圾邮件...

您可以发送表情符号表达您的感受

另外,看着表情符号数量像这样攀升也很有趣。 我从未见过一个问题评论如此迅速地获得如此多的人气!

我录制了实时更新的小视频https://youtu.be/JLBrgPjeGhc

有人可以取消我对这件事的订阅吗?

@DanielRosenwasser如果您不是在开玩笑,或者对于其他想要退订但不知道如何退订的人,您可以在右侧边栏中查找此按钮:

image

更不用说电子邮件中有一个链接:

image

这是个玩笑,我正在研究提案。

@RyanCavanaugh解锁此问题后:
martian

看到这终于登陆真的很兴奋! 🎈🎉

等不及匹配的 VSCode 快速修复😆

image

我要去@@ RyanCavanaugh因为他可能没有订阅这个帖子,我想变得粗鲁! (和@DanielRosenwasser很好的衡量标准)

@kitsonk 别傻了。 人们可以自由退订,不受骚扰。

@kitsonk ,为什么 RyanCavanaugh 或 DanielRosenwasser 会退订此线程? Ryan 解锁了这个问题,Daniel 在你上面回复了三条评论。

即使他们被取消订阅,也没有必要通过向他们发送垃圾邮件来导致更多的通知疲劳。

显然 GitHub 是一个讽刺幽默的可怕地方,哎呀......

非常感谢我们在 TC39 的冠军们找出了这个新语言功能的令人讨厌的细节!

thanks

我认为@kitsonk只是在开玩笑,就像我一样。 虽然我们对兴奋有点傻,但这是一个温和的提醒,要按照 CoC 保持文明。

@DanielRosenwasser
我可以试试这个吗? 或者@rbuckton还有另一个存在的分支🙋🏻‍♂️


好的,我明白了https://github.com/microsoft/TypeScript/commits/optionalChainingStage1 🤦🏻‍♂️

是的,不幸的是,这已经过时了。

我刚刚意识到这个问题是 5 年前打开的。 :吃惊:

@rbuckton

是的,不幸的是,这已经过时了。

我可以试试这个吗?

抱歉@jhpratt @MatthiasKunnen我忘了我们在 GitHub 上的上下文并不相同。 我已经在这里蹦蹦跳跳了很长一段时间,并与 Ryan 和 Daniel IRL 一起度过了一段时间,并短暂地参加了最近引起我误解的内部笑话的事件。 道歉。

整个问题虽然展示了 TypeScript 设计原则的有趣考古学。 Ryan 提出这个问题的时候,TypeScript _可能_实际上已经考虑了早于 ECMAScript 认真考虑的语法。 大约在那个时候,TypeScript 内部正在学习一些预测 ES2015 语法的课程,这些课程对语言产生了严重影响。 团队决定在 TC39 第 3 阶段提案提出之前不考虑纳入。

虽然@RyanCavanaugh可以解决这个问题,而且我知道他已经接近该提案的情况,但我怀疑该团队在 2014 年实施的内容不太可能与当前的第 3 阶段提案 100% 兼容. 因此,虽然这当然是一个庆祝活动,但也值得庆幸的是,我们没有 5 年的 TypeScript 代码使用安全导航运算符,这与提案中的行为不完全一致。

🙏

westwing

现在是删除等待 TC39 标签的时刻吗? 🤔

现在是删除等待 TC39 标签的时刻吗? 🤔

并添加ship-it

哇哇哇

最后。 从字面上看,是在询问有关可选链接的问题,想知道什么时候会是第 3 阶段,然后砰! 感谢所有努力推动它的人!

如何静音这个线程? :)

@opnksyn如果您不关心垃圾邮件的兴奋,有一个功能可以在此问题关闭时收到通知。 这将防止所有评论出现在通知中,就像您刚刚所做的一样😄

image

image

任何已定义的发射解决方案?
这样的事情可能很有趣:

function __chain<T extends object, U>(value: T|null|undefined, callback: (value: T) => U): U|undefined {
    if (value !== null && value !== undefined) {
        return callback(value);
    }

    return undefined;
}

type Foo = { x?: { y?: { z?: () => number } } }

const foo: Foo|null = { }

// foo?.x?.y?.z?()
const value = __chain(foo, _a => __chain(_a.x, _b => __chain(_b.y, _c => __chain(_c.z, _d => _d()))));

我更喜欢 Babel 所做的,因为它避免了创建不必要的函数作用域:

var _foo, _foo$x, _foo$x$y, _foo$x$y$z;

// foo?.x?.y?.z?.()
(_foo = foo) === null || _foo === void 0 ? void 0
    : (_foo$x = _foo.x) === null || _foo$x === void 0 ? void 0
    : (_foo$x$y = _foo$x.y) === null || _foo$x$y === void 0 ? void 0
    : (_foo$x$y$z = _foo$x$y.z) === null || _foo$x$y$z === void 0 ? void 0
    : _foo$x$y$z.call(_foo$x$y);

我更喜欢 Babel 所做的,因为它避免了创建不必要的函数作用域:

var _foo, _foo$x, _foo$x$y, _foo$x$y$z;

// foo?.x?.y?.z?.()
(_foo = foo) === null || _foo === void 0 ? void 0
  : (_foo$x = _foo.x) === null || _foo$x === void 0 ? void 0
  : (_foo$x$y = _foo$x.y) === null || _foo$x$y === void 0 ? void 0
  : (_foo$x$y$z = _foo$x$y.z) === null || _foo$x$y$z === void 0 ? void 0
  : _foo$x$y$z.call(_foo$x$y);

我同意,@ExE-Boss。 我觉得避免创建不必要的函数范围是理想的(即使它使编译的代码有点难看)

当我们谈论编译代码时,性能和对 ES 规范的遵守肯定会胜过可读性。

Babel 编译的代码是否有任何理由将nullvoid 0与三等号而不是简单的== null进行比较?

Babel 编译的代码是否有任何理由将nullvoid 0与三等号而不是简单的== null进行比较?

@proteria我只是快速查看了 __Babel__ 的可选链接代码; 似乎如果您传入loose选项并将其设置为真实值,则不会产生严格的相等检查

这是因为document.all (或者是迂腐的, [[IsHTMLDDA]] 内部 slot ),一个在语言中得到特殊处理以实现向后兼容性的怪癖。

document.all == null // true
document.all === null || document.all === undefined // false

在可选链提案中

document.all?.foo === document.all.foo

但是document.all == null ? void 0 : document.all.foo会错误地返回void 0 。 在松散模式下,为了简单/性能/生成的代码大小,规范的这个细节被忽略了,因为大多数人无论如何都不必处理document.all

当然, document.all案例可能很特别吗? 它不需要太多额外的代码,只需几行即可检查对象和属性。

除了document.all可以分配给变量,并且跟踪它的使用位置需要类型系统,这就是 Babel 默认输出的原因:

(_prop = prop) === null || _prop === void 0 ? void 0 : _prop./* do stuff */;

我对此很清楚。 Babel 没有类型系统,TypeScript 有。 也许它不像我说的那么简单,但我想已经有代码可以用于某些情况,可以跟踪使用情况。

实际上,您不需要类型系统来跟踪document.all变量,因为特殊行为实际上是在HTMLAllCollection上,而不是document.all上。

所以你应该能够做一个instanceof HTMLAllCollection支票,你会很成功。

是的,但是......当你可以做=== null || === void 0 $ 时,你为什么要做instanceof #$ ? 当然,这更简单。

当然 - 我只是指出你不需要类型系统来跟踪document.all :)

就我个人而言,我很想说只是打破它,看看谁在抱怨,但它在规范中,所以最容易坚持下去。

@noppa可以在编译时执行。 如果foo instanceof HTMLAllCollectiontrue ,则发出foo === null || foo === void 0 ,否则我们可以_安全地_发出foo == null

@G-Rath 不管你喜不喜欢,弃用并不意味着它不应该工作。 TypeScript 应该与 JavaScript 保持兼容。

@jhpratt但这目前违反了TypeScript Design Non‑Goals

此外,对于可以分配HTMLAllCollection foo === null || foo === void 0 ,例如。 anyobject ,所以我认为这不值得。

我想你指的是非目标(5)

在程序中添加或依赖运行时类型信息,或根据类型系统的结果发出不同的代码。 相反,鼓励不需要运行时元数据的编程模式。

虽然我同意这肯定会根据类型发出不同的代码,但这只是为了减少代码大小。 尽管正如您所指出的,它并不像检查HTMLAllCollection那样简单。

公平地说,TS _has_ 拒绝了一个使用类型信息的潜在缩小器,这(某种程度上)是相关的——发出== null的主要原因是根据类型信息减少代码大小。

如果这_没有_实现,如果 lang 团队在 tsconfig 中添加一个类似于 Babel 的“松散”选项的选项,那就太好了。

快速检查后, terser自动将foo === null || foo === undefined转换为foo == null ,由于这种极端情况,这是不安全的。

如果这没有实现,那么如果 lang 团队在 tsconfig 中添加一个类似于 Babel 的“松散”选项的选项,那就太好了。

与此相关的是,我们中的许多人将 TypeScript 用于构建工具和移动应用程序,它们都不必担心浏览器的限制!

事实上,我们同时使用 TS 和 Babel,所以也许这些选项之一应该是将操作符传递给 Babel/底层运行时!

@fbartho

事实上,我们同时使用 TS 和 Babel,所以也许这些选项之一应该是将操作符传递给 Babel/底层运行时!

我不明白这个评论。 你不需要任何额外的选项来将操作符传递给 Babel; 如果你为 Babel 设置了 TypeScript,那么你已经有了noEmit: true ,它已经将 _everything_ 传递给了 Babel。

@Zarel Babel 的 TypeScript 实现缺少我们的代码库已经依赖的几个特性,包括命名空间和 const 枚举。 我们使用启用了发射的 TSC,并将 Babel 作为第二个转换。 (我们正在努力摆脱命名空间,但尚不清楚我们是否能够摆脱所有不匹配的功能)

来到这个线程的人应该从早期的第 3 阶段公告开始并阅读从那里开始的评论(归咎于 GitHub 隐藏了大量的用户内容,没有直接的方法来加载所有内容)

很棒的功能-“可选链接”/“安全导航”。 特别是在 TypeScript 严格模式下。 很高兴听到这将很快实施。 ❤️

这把我带到了这里,我希望它会得到支持。 只是一个用例:

预计在 TypeScript 3.7 中。

document.querySelector('html')?.setAttribute('lang', 'en');

VS

目前在 TypeScript 3.5 中。

const htmlElement = document.querySelector('html');
if (htmlElement) {
  htmlElement.setAttribute('lang', 'en');
}

这可以正常工作吗? 还是这仍然是TypeError: Cannot read property 'setAttribute' of null.?操作。 应该在 null / undefined 之后取消进一步的链。

class Test {
  it() {
    console.log('One');
    document.querySelector('html')?.setAttribute('lang', 'en');
    console.log('Two');
  }
}
new Test().it();

我期望以下:
如果 html 元素不存在 (null)。 控制台应记录OneTwo ,并且不会尝试调用setAttribute方法。 (没有错误)。
我理解正确吗?

@domske仅供参考,这不是严格意义上的 TS 功能; 这是一个 JS 功能。

根据TC39 提案,语法将是:

document.querySelector('html')?.setAttribute?.('lang', 'en');

讨论又开始循环了,所以我们回到了锁定状态。

我真的恳求任何想在 100 多条评论长的 GitHub 线程中发表评论的人,以真正承诺首先阅读所有先前的评论。 您的问题及其答案可能会在那里找到!

此页面是否有帮助?
0 / 5 - 0 等级