Typescriptは?をサポートしていますか?オペレーター?(そして、それは何と呼ばれますか?)


337

Typescriptは現在(または予定されています)の安全なナビゲーション演算子をサポートしていますか??.

つまり:

var thing = foo?.bar
// same as:
var thing = (foo) ? foo.bar : null;

また、この演算子にはより一般的な名前があります(グーグルで検索するのは難しいためです)。


3
@mattytommoあなたはそれをC#で持っています、それはnull合体演算子と呼ばれ、?? 構文weblogs.asp.net/scottgu/archive/2007/09/20/...
basarat

2
@BasaratAli残念ながら、合体はで問題ありませんが、property ?? property2試行property.company ?? property1.companyしてpropertynullだった場合は、NullReferenceException
mattytommoが2013年

1
@mattytommoありがとう、私は今それを手に入れました '?。' チェーン内のすべてのnull参照を実際に吸収します。甘い。
バサラト2013年

9
@mattytommoこれは現在C#に存在します:msdn.microsoft.com/en-us/library/dn986595.aspx
Highmastdon

9
私たちを訪問したマイクロソフトの担当者は、疑問符がエルビスの髪の毛と彼が歌っているマイクのように見えるため、エルビスのオペレーターと呼んでいました...
Zymotik

回答:


168

更新:TypeScript 3.7以降でサポートされ、オプションチェーンと呼ばれますhttps : //www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining

TypeScript言語仕様では、それへの参照をまったく見つけることができません。

CoffeeScriptでこの演算子を何と呼ぶか​​については、存在演算子(具体的には、存在演算子の「アクセサーバリアント」)と呼ばれます。

オペレーターに関するCoffeeScriptのドキュメントから:

存在演算子のアクセサーバリアントを?.使用して、一連のプロパティでnull参照を吸収できます。.ベース値がnullまたは未定義の可能性がある場合は、ドットアクセサーの代わりに使用します

したがって、存在演算子のアクセサーバリアントは、この演算子を参照する適切な方法のようです。TypeScriptは現在それをサポートしていないようです(ただし、他の人がこの機能への要望を表明しています)。


28
「存在演算子のアクセサーバリアント」。当然。とてもキャッチーで、忘れることはほとんど不可能です。:)。非常に徹底した答えをありがとう。
Marty Pitt、

1
@MartyPitt確かに!私は同意します。a)このような演算子の幅広い採用(C#をお願いします!)とb)より良い名前(リンクされたブログ投稿からの「安全なナビゲーション」演算子には素晴らしいリングがあります)。
2013年

2
Angularはこれをテンプレートに実装しています:angular.io/guide/…– Enzoaeneas 18
20:23

5
他のいくつかの言語では、「Elvis」演算子と呼ばれています
k0enf0rNL '26

4
TypeScript 3.7.0github.com/microsoft/TypeScript/issues/…)で発表されました
c_froehlich '20年

146

シングルほどではありませんが、機能します。

var thing = foo && foo.bar || null;

&&はいくつでも使用できます。

var thing = foo && foo.bar && foo.bar.check && foo.bar.check.x || null;

33
&&は、ステートメントが真である限り評価します。trueの場合、最後の値を返します。falseの場合、falseと評価された最初の値を返します。それは、0、null、falseなどの場合があります。trueと評価される最初の値を返します。
A. KR

34
バーが定義されている場合はうまく機能しませんが、評価結果はfalseになります(boolean falseまたは0など)。
mt_serg 2017

96

これはECMAScriptのオプションチェーン仕様で定義されているため、これについて説明するときには、オプションチェーンを参照する必要があります。ありそうな実装:

const result = a?.b?.c;

TypeScriptチームは、ECMAScript仕様が厳格化されるのをTypeScriptチームが待っているため、その実装は将来、非破壊的なものになる可能性があります。彼らが今何かを実装した場合、ECMAScriptが仕様を再定義すると、大きな変更が必要になります。

オプションの連鎖仕様を参照

TypeScriptチームは、標準のJavaScriptになることが決してない場合、適切と思われるように実装できますが、将来のECMAScriptの追加では、他の多くの機能と同様に、早期アクセスを許可してもセマンティクスを保持したいと考えています。

ショートカット

そのため、次のような型変換を含め、JavaScriptのすべてのファンキーな演算子を使用できます...

var n: number = +myString; // convert to number
var b: bool = !!myString; // convert to bool

手動による解決策

しかし、質問に戻ります。私がJavaScript(したがってTypeScript)で同様のことを行う方法の鈍い例がありますが、あなたが本当に望んでいる機能であるため、それが優雅であるとは絶対に示唆していません。

(foo||{}).bar;

その場合は、fooあるundefined結果であるundefinedと場合fooに定義され、名前のプロパティがあるされbarた値を持っている、結果はその値です。

私は入れJSFiddleに例を

これは、より長い例ではかなり大ざっぱに見えます。

var postCode = ((person||{}).address||{}).postcode;

チェーン機能

仕様がまだ空いている間に短いバージョンが必要な場合は、この方法を使用することがあります。これは式を評価し、チェーンが満たされなかったり、null / undefinedになったりした場合にデフォルトを返します(!=ここでは重要です。ここでは少し前向きなジャグリングをしたいので使用しませ!==)。

function chain<T>(exp: () => T, d: T) {
    try {
        let val = exp();
        if (val != null) {
            return val;
        }
    } catch { }
    return d;
}

let obj1: { a?: { b?: string }} = {
    a: {
        b: 'c'
    }
};

// 'c'
console.log(chain(() => obj1.a.b, 'Nothing'));

obj1 = {
    a: {}
};

// 'Nothing'
console.log(chain(() => obj1.a.b, 'Nothing'));

obj1 = {};

// 'Nothing'
console.log(chain(() => obj1.a.b, 'Nothing'));

obj1 = null;

// 'Nothing'
console.log(chain(() => obj1.a.b, 'Nothing'));

1
興味深いのが、私の場合は(this.loop || {}).nativeElement言って Property 'nativeElement' does not exist on type '{}'. any this.loopTYPEOF angular.io/api/core/ElementRef
kuncevic.dev

@Kuncevic-次のいずれかを行う必要があります... 1)の代わりに互換性のあるデフォルトを提供する{}か、2)型アサーションを使用してコンパイラーを無音にします。
フェントン2017

仮定fooは実際に有用なオブジェクトです:と同じ型ではない(foo || {}).barため、通常はtypescriptでコンパイルされ{}ませんfoo。これが、@ VeganHunterのソリューションが回避しようとしている問題です。
Simon_Weaver

1
@Simon_Weaver(foo || {bar})。barを使用すると、コンパイラーがスムーズに実行され、冗長性は許容できると思います。
Harps

@harpsは実際には、barが変数として定義されている場合にのみコンパイルされますが、ほとんどの場合そうではありません
Simon_Weaver

83

更新:はい、現在サポートされています!

TypeScript 3.7でリリースされたばかりです:https : //devblogs.microsoft.com/typescript/announcing-typescript-3-7/

これはオプションのチェーンと呼ばれますhttps : //devblogs.microsoft.com/typescript/announcing-typescript-3-7/#optional-chaining

これを使用すると、次のようになります。

let x = foo?.bar.baz(); 

以下と同等です。

let x = (foo === null || foo === undefined) ?
    undefined :
    foo.bar.baz();

古い答え

あなたはあなたの意見/欲望を表明することができるgithubにこれについてのオープン機能リクエストがあります:https//github.com/Microsoft/TypeScript/issues/16


36

2019年11月13日を編集!

2019年11月5日の時点でTypeScript 3.7が出荷され、オプションの連鎖演算子🎉🎉🍾🍾🎉!!!がサポート されるようになりました。?.

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining


歴史的目的のみ:

編集:私はfraczコメントのおかげで答えを更新しました。

TypeScript 2.0がリリースさ!.れました?.(C#のセーフナビゲーター)と同じではありません

詳細については、この回答を参照してください:

https://stackoverflow.com/a/38875179/1057052

これは、値がnullまたは未定義ではないことをコンパイラーに通知するだけです。これは、値がnullまたは未定義であるかどうかをチェックしません

TypeScript非nullアサーション演算子

// Compiled with --strictNullChecks
function validateEntity(e?: Entity) {
    // Throw exception if e is null or invalid entity
}

function processEntity(e?: Entity) {
    validateEntity(e);
    let s = e!.name;  // Assert that e is non-null and access name
}

4
値が定義されて?いると主張するのと同じではありません。?静かに失敗するか、falseと評価されることが期待されます。とにかく、知っておくと良い。
fracz 2016年

1
今考えてみると... C#演算子が行う「安全なナビゲーション」を行わないため、この答えはかなり無意味です。
Jose A

5
しかし、これは私の質問に答えました。知っていましたか。C#から、typescriptで試してみました。うまくいきませんでしたが、見ました!存在したが、それが何をしたのか知らなかった。私はそれが同じであるかと思って、グーグル検索をしました、そして、いいえ、彼らは違うと私に知らせたこの質問への私の道を見つけました。
Llewey 2017

11

Elvis(?。)オプションの連鎖演算子はTypeScript 3.7でサポートされています。

これを使用してnull値を確認できますcats?.miows。catsがnullまたは未定義の場合はnullを返します。

オプションのメソッド呼び出しにも使用できcats.doMiow?.(5)ます。存在する場合はdoMiowを呼び出します。

プロパティへのアクセスも可能ですcats?.['miows']

リファレンス:https : //devblogs.microsoft.com/typescript/announcing-typescript-3-7-beta/


私を訂正してください、しかしエルビスのオペレーターは少なくともコトリンにい?:ます。リファレンスはありますか?
rekire


1
-それはすぐに、プレーンJSでサポートされますdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
Mottie

1
活字体3.7のリリースはそれを言及のお知らせ:devblogs.microsoft.com/typescript/announcing-typescript-3-7
ジェルジBalássy

10

?.TypeScript バージョン2.0では演算子はサポートされていません。

だから私は次の関数を使います:

export function o<T>(someObject: T, defaultValue: T = {} as T) : T {
    if (typeof someObject === 'undefined' || someObject === null)
        return defaultValue;
    else
        return someObject;
}

使用法は次のようになります。

o(o(o(test).prop1).prop2

さらに、デフォルト値を設定できます。

o(o(o(o(test).prop1).prop2, "none")

Visual StudioのIntelliSenseで非常にうまく機能します。


1
これがまさに私が探していたものです!typescript 2.1.6で動作します。
Rajab Shakirov 2017年

5
またはあなたはそれを呼び出すことができますelvis<T>;-)
Simon_Weaver 2018年

3
Simon_Weaver、私はそれを「悲しい道化師」と呼びます:o(
VeganHunter '13 / 11/18

5

ついに登場!

以下にいくつかの例を示します。

// properties
foo?.bar
foo?.bar()
foo?.bar.baz()
foo?.bar?.baz()

// indexing
foo?.[0]
foo?.['bar']

// check if a function is defined before invoking
foo?.()
foo.bar?.()
foo?.bar?.()

しかし、それはあなたの仮定とまったく同じようには機能しません。

評価する代わりに

foo?.bar

この小さなコードスニペットに、私たちはすべて書くことに慣れています

foo ? foo.bar : null

実際に評価する

(foo === null || foo === undefined) ?
    undefined :
    foo.bar

これは、空の文字列、0またはfalseなどのすべての偽の値に対して機能します。

彼らがなぜそれをコンパイルしないのかについての説明はありません foo == null


3

Typetraでディーププロパティへのタイプセーフなアクセスを提供できるPhonetradrの作業中に、このutilメソッドを作成しました。

/**
 * Type-safe access of deep property of an object
 *
 * @param obj                   Object to get deep property
 * @param unsafeDataOperation   Function that returns the deep property
 * @param valueIfFail           Value to return in case if there is no such property
 */
export function getInSafe<O,T>(obj: O, unsafeDataOperation: (x: O) => T, valueIfFail?: any) : T {
    try {
        return unsafeDataOperation(obj)
    } catch (error) {
        return valueIfFail;
    }
}

//Example usage:
getInSafe(sellTicket, x => x.phoneDetails.imeiNumber, '');

//Example from above
getInSafe(foo, x => x.bar.check, null);


涼しい!!注意点はありますか?私には約20のゲッターを記述するラッパークラスがあり、そのすべてに次のタイプの戻り値があり、すべてのフィールドはnullチェックされている必要がありますreturn this.entry.fields.featuredImage.fields.file.url;
Drenai

唯一の警告はパフォーマンスへの影響である可能性がありますが、私はさまざまなJITがこれをどのように処理するかについて話す資格がありません。
Ray Suelzer、2018

2

このアプローチはお勧めしません(パフォーマンスの問題に注意してください)が、spreadオペレーターを使用してオブジェクトのシャロークローンを作成し、そのプロパティにアクセスできます。

 const person = { personId: 123, firstName: 'Simon' };
 const firstName = { ...person }.firstName;

これは、「firstName」のタイプが「伝播」されるため機能します。

find(...)nullを返すことができる式があり、そこから単一のプロパティが必要な場合、これを最も頻繁に使用します。

 // this would cause an error (this ID doesn't exist)
 const people = [person];
 const firstName2 = people.find(p => p.personId == 999).firstName;

 // this works - but copies every property over so raises performance concerns
 const firstName3 = { ...people.find(p => p.personId == 999) }.firstName;

typescriptが型を推論する方法にはいくつかのエッジケースがあり、これはコンパイルされませんが、これは通常は機能するはずです。



0

以前に答えたように、それは現在検討ですが、数年前から水中死んでいます。

既存の回答に基づいて、私が考えることができる最も簡潔な手動バージョンは次のとおりです。

jsfiddle

function val<T>(valueSupplier: () => T): T {
  try { return valueSupplier(); } catch (err) { return undefined; }
}

let obj1: { a?: { b?: string }} = { a: { b: 'c' } };
console.log(val(() => obj1.a.b)); // 'c'

obj1 = { a: {} };
console.log(val(() => obj1.a.b)); // undefined
console.log(val(() => obj1.a.b) || 'Nothing'); // 'Nothing'

obj1 = {};
console.log(val(() => obj1.a.b) || 'Nothing'); // 'Nothing'

obj1 = null;
console.log(val(() => obj1.a.b) || 'Nothing'); // 'Nothing'

プロパティが見つからないというエラーが発生した場合は、黙って失敗します。デフォルト値を決定するための標準構文にフォールバックしますが、これも完全に省略できます。


これは単純なケースで機能しますが、関数を呼び出して結果のプロパティにアクセスするなどのより複雑なものが必要な場合は、他のエラーも同様に飲み込まれます。悪いデザイン。

上記の場合、ここに投稿された他の回答の最適化されたバージョンがより良いオプションです。

jsfiddle

function o<T>(obj?: T, def: T = {} as T): T {
    return obj || def;
}

let obj1: { a?: { b?: string }} = { a: { b: 'c' } };
console.log(o(o(o(obj1).a)).b); // 'c'

obj1 = { a: {} };
console.log(o(o(o(obj1).a)).b); // undefined
console.log(o(o(o(obj1).a)).b || 'Nothing'); // 'Nothing'

obj1 = {};
console.log(o(o(o(obj1).a)).b || 'Nothing'); // 'Nothing'

obj1 = null;
console.log(o(o(o(obj1).a)).b || 'Nothing'); // 'Nothing'

より複雑な例:

o(foo(), []).map((n) => n.id)

また、別の方法でLodashのようなものを使用することもでき_.get()ます。簡潔ですが、コンパイラは使用されたプロパティの有効性を判断できません。

console.log(_.get(obj1, 'a.b.c'));

0

まだ(2019年9月現在)ですが、「セーフナビゲーションオペレーター」は現在ステージ3にあるため、TypeScriptで実装されています。

この問題の更新を監視します。

https://github.com/microsoft/TypeScript/issues/16

いくつかのエンジンには初期の実装があります:

JSC:https : //bugs.webkit.org/show_bug.cgi?id=200199

V8:https : //bugs.chromium.org/p/v8/issues/detail?id=9553

SM:https : //bugzilla.mozilla.org/show_bug.cgi?id=1566143

https://github.com/tc39/proposal-optional-chaining/issues/115#issue-475422578から

今すぐプラグインをインストールしてサポートすることができます:

npm install --save-dev ts-optchain

tsconfig.jsonで:

// tsconfig.json
{
    "compilerOptions": {
        "plugins": [
            { "transform": "ts-optchain/transform" },
        ]
    },
}

この回答は今後6か月ほどで古くなると思いますが、うまくいけば、その間誰かを助けるでしょう。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.