Typescriptでは、!(感嘆符/バング)メンバーを逆参照するときの演算子?


453

tslintルールのソースコードを見ると、次のステートメントに出くわしました。

if (node.parent!.kind === ts.SyntaxKind.ObjectLiteralExpression) {
    return;
}

!後の演算子に注意してくださいnode.parent。面白い!

最初に、現在インストールされているバージョンのTS(1.5.3)でローカルにファイルをコンパイルしてみました。結果のエラーは、強打の正確な場所を指しています。

$ tsc --noImplicitAny memberAccessRule.ts 
noPublicModifierRule.ts(57,24): error TS1005: ')' expected.

次に、問題なくコンパイルできる最新のTS(2.1.6)にアップグレードしました。TS 2.xの機能のようです。しかし、トランスパイレーションは強打を完全に無視し、次のJSをもたらしました:

if (node.parent.kind === ts.SyntaxKind.ObjectLiteralExpression) {
    return;
}

私のGoogle fuはこれまでのところ失敗しています。

TSの感嘆符演算子とは何ですか?それはどのように機能しますか?

回答:


688

これがnull以外のアサーション演算子です。これは、コンパイラに指示する方法である「この表現はすることはできませんnullか、undefinedそれがあることの可能性について文句を言わない、ここnullundefined。」型チェッカーは、その決定自体を行うことができないことがあります。

ここで説明します

新しい!ポストフィックス式演算子を使用して、型チェッカーがその事実を結論付けることができないコンテキストで、そのオペランドがnullでなく、未定義でないことを表明できます。具体的には、この操作x!により、xwith nullおよびundefinedexcludeのタイプの値が生成されます。フォーム<T>xおよびの型アサーションと同様にx as T!null以外のアサーション演算子は、出力されたJavaScriptコードから削除されるだけです。

その説明では、「アサート」という用語の使用は少し誤解を招くと思います。テストが実行されるという意味はなく、開発者がそれを主張するという意味で「アサート」します。実際、最後の行は、JavaScriptコードが発行されないことを示しています。


102
「アサート」のあいまいさに対する適切な呼びかけ。
Estus Flask 2017

8
良い説明。アフターをconsole.assert()追加する前に、問題の変数に対してaを実行することをお勧め!します。add !は、nullチェックを無視するようコンパイラーに指示しているため、JavaScriptでnoopにコンパイルされます。したがって、変数がnullでないことが確実でない場合は、明示的なアサートチェックを行うことをお勧めします。
Jayesh、2017

12
動機付けの例として:dict.has(key) ? dict.get(key) : 'default';TSコンパイラのようなコードで新しいESマップタイプを使用しても、get呼び出しがnull / undefinedを返さないことは推測できません。dict.has(key) ? dict.get(key)! : 'default';タイプを正しく絞り込みます。
kitsu.eb

1
Elvis演算子が2項演算子を参照するように、この演算子に俗語はありますか?
エバクニン

@Jayeshは、console.assert()の良い方法を拡張できますか、例を投稿できますか?
クリストファーフランシスコ

168

ルイスの答えは素晴らしいですが、私はそれを簡潔に要約しようとするつもりだと思いました:

bang演算子は、コンパイラに、そうでなければ要求される可能性がある「nullではない」制約を一時的に緩和するように指示します。それはコンパイラーに言います:「開発者として、私はこの変数が現在nullであってはならないことをあなたよりよく知っています」。


85
その後、開発者として、あなたはめちゃくちゃになりました。
Mike Chamberlain

9
または、コンパイラとして、めちゃくちゃになっています。コンストラクターがプロパティを初期化しないが、ライフサイクルフックが初期化し、コンパイラーがこれを認識しない場合。
ムクス

26
これは、TSコンパイラの責任ではありません。他の一部の言語(C#など)とは異なり、JS(したがってTS)では、使用前に変数を初期化する必要はありません。または、別の見方をすると、JSでは、varまたはで宣言されたすべての変数letが暗黙的にに初期化されundefinedます。さらに、クラスインスタンスプロパティはそのように宣言できるためclass C { constructor() { this.myVar = undefined; } }、完全に合法です。最後に、ライフサイクルフックはフレームワークに依存します。たとえば、AngularとReactはそれらを異なる方法で実装します。そのため、TSコンパイラはそれらについて推論することは期待できません。
マイクチェンバレン

1
TSでの制御フローベースの型分析の驚異を考慮したbang演算子の有効な使用例はありますか?
ユージーンカラタエフ

1
@EugeneKarataevはい、フレームワークは内部の変数を初期化することが多く、ts制御フロー分析はそれをキャッチできません。その使用量は確かに削減されますが、必要なインスタンスに出くわすことになります。
arg20
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.