Güvenli gezinme operatörünü (?.) Veya (!.) Ve boş özellik yollarını yazın


111

Angular2 şablonlarında güvenli operatör ?.çalışır, ancak component.tsTypeScript 2.0 kullanıldığında çalışmaz. Ayrıca, güvenli navigasyon operatörü (!.) Çalışmaz.

Örneğin...

Bu TypeScript

if (a!.b!.c) { }

bu JavaScript'e derler

if (a.b.c) { }

Ama çalıştırdığımda şu hatayı alıyorum:

Tanımsız 'b' özelliği okunamıyor

Aşağıdakilere herhangi bir alternatif var mı?

if (a && a.b && a.b.c) { }

7
Dizgi işleçleri yalnızca derleme için mevcuttur, derlenmiş javascript'te mevcut değildir. Gönderdiğiniz hata bir çalışma zamanı hatasıdır.
Nitzan Tomer

o (o (o (o (test) .level1) .level2) .level3 veya o (o (o (o (a) .b) .c) .d stackoverflow.com/a/35169378/3914072 bu çözüm harika çalışıyor bizim için derleme zamanında ve güvenli türlerle
Rajab Şakirov

Yanıtlar:


74

TypeScript 3.7 piyasaya sürüldüğünden beri isteğe bağlı zincirleme kullanabilirsiniz.

Mülk örneği:

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

Bu şuna eşittir:

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

Ayrıca şunları da arayabilirsiniz:

İsteğe Bağlı Çağrı

function(otherFn: (par: string) => void) {
   otherFn?.("some value");
}

otherFn yalnızca otherFn'nin null veya undefined değerine eşit olmaması durumunda çağrılır

IF deyiminde isteğe bağlı zincirleme kullanın

Bu:

if (someObj && someObj.someProperty) {
    // ...
}

şimdi bununla değiştirilebilir

if (someObj?.someProperty) {
    // ...
}

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


bu, derleme sırasında Expression beklenen hatasını atmaya devam ediyor , "typcript" !yerine kullanırsam işe yarıyor ?: "~ 3.5.3"
Scaramouche

3
@Scaramouche bunun nedeni isteğe bağlı zincirleme operatörünün ( ?.) 3.7'de tanıtılması ve 3.5.3 çalıştırmanızdır. 3.7'den önceki sürümlerde TypeScript ?, if form ( foo ? bar : baz) stenosunun stenografi olarak kullanıldığını varsayar ve bu nedenle ifadenin geri kalanını bekler
George

134

!olan boş olmayan sav operatörü (sonradan düzeltme ifade) - bu sadece o taşı yazın söyleyerek sen emin adeğildir nullya undefined.

işlem ile ve hariç tutulan a!türünde bir değer üretiranullundefined


İsteğe bağlı zincirleme son olarak yapılan bu (typescript için 3.7 ) 🎉

İsteğe bağlı zincirleme operatörü ?., bağlı nesneler zincirinin derinliklerinde bulunan bir özelliğin değerinin, zincirdeki her bir referansın geçerli olduğunu açıkça doğrulamak zorunda kalmadan okunmasına izin verir. ?.Benzer operatörün işlevleri .dışında zincirleme operatör yerine bir referans nullish (eğer bir hata neden nullya da undefined), bir geri dönüş değeri ile ifade kısa devreler undefined. İşlev çağrılarıyla birlikte kullanıldığında undefined, verilen işlev yoksa geri döner .

Sözdizimi :

obj?.prop // Accessing object's property
obj?.[expr] // Optional chaining with expressions
arr?.[index] // Array item access with optional chaining
func?.(args) // Optional chaining with function calls

Dikkat edin :

İsteğe bağlı zincirleme, atamanın sol tarafında geçerli değildir

const object = {};
object?.property = 1; // Uncaught SyntaxError: Invalid left-hand side in assignment

12
Bunun durumunu kontrol etmek isteyenler için. TypeScript, ECMAScript desteği ekledikten sonra onu eklemeyi planlıyor. ECMAScript, bu sorunu burada izliyor .
Pace

o (o (o (o (test) .level1) .level2) .level3 veya o (o (o (o (a) .b) .c) .d stackoverflow.com/a/35169378/3914072 bu çözüm harika çalışıyor bizim için derleme zamanında ve güvenli türlerle
Rajab Şakirov

yeni özelliğin oyun alanı (5 Kasım'da yayınlanacak): typescriptlang.org/play/?ts=3.7.0-pr-33294-11#code/…
Nisim Joseph

1
@ aleksey-l: "Son güncelleme" öncesinde malzemelerin üstünü çizmek kötü bir fikir olmayabilir. Yukarıdan aşağıya (yani çok hızlı) okumayan insanlara yardımcı olacaktır! :)
Aidin

1
Bu çok havalı - işlev çağrıları için kullanabileceğiniz hakkında hiçbir fikrim yoktu. Bir çözüm arıyordu ve cevabınızla karşılaştım, bu yüzden teşekkür ederim!
Geoff Davids

22

Güncelleme:

3.7 sürüm https://github.com/microsoft/TypeScript/issues/33352 kapsamında planlanmıştır


Bunun gibi özel bir işlev yazmayı deneyebilirsiniz.

Yaklaşımın temel avantajı, tip kontrolü ve kısmi zeka.

export function nullSafe<T, 
    K0 extends keyof T, 
    K1 extends keyof T[K0],
    K2 extends keyof T[K0][K1],
    K3 extends keyof T[K0][K1][K2],
    K4 extends keyof T[K0][K1][K2][K3],
    K5 extends keyof T[K0][K1][K2][K3][K4]>
    (obj: T, k0: K0, k1?: K1, k2?: K2, k3?: K3, k4?: K4, k5?: K5) {
    let result: any = obj;

    const keysCount = arguments.length - 1;
    for (var i = 1; i <= keysCount; i++) {
        if (result === null || result === undefined) return result;
        result = result[arguments[i]];
    }

    return result;
}

Ve kullanım (5 parametreye kadar destekler ve genişletilebilir):

nullSafe(a, 'b', 'c');

Oyun alanı örneği .


1
Bu iyi çalışıyor, ancak bir sorunu var - VSCode, tuşlardan birinde "Tüm Referansları Bul" kullanılırken bunu göstermeyecektir.
Ivan Koshelev

Ne tür bir tür denetimi? Dizeleri parametre olarak geçirdiğiniz an, türü kaybettiniz. Öyleyse, lodash.get
un33k

16

Harici bir kitaplık kullanan diğer bir alternatif Lodash'tan _.has () ' dır .

Örneğin

_.has(a, 'b.c')

eşittir

(a && a.b && a.b.c)

DÜZENLEME: Yorumlarda belirtildiği gibi, bu yöntemi kullanırken Typescript'in tür çıkarımını kaybedersiniz. Örneğin, kişinin nesnelerinin düzgün bir şekilde yazıldığını varsayarsak, z nesnesi b'nin alanı olarak tanımlanmazsa (a && ab && abz) ile bir derleme hatası alırsınız. Ancak _.has (a, 'b.z') kullanıldığında bu hatayı almazsınız.


Güzel bir ipucu, başka bir çözüm bulunmamakla birlikte, bu iyi bir alternatif gibi görünüyor.
Dimas Crocco

3
Ancak TypeScript'in tür çıkarımıyla nasıl çalışır?
VitalyB

@VitalyB muhtemelen, lodash'ın ilgili arayüzünü içe aktarmak anlamına geliyor. ('lodash'tan _ olarak içe aktar;)
Ankur Arora

1
@AnkurArora, Sanırım VitalyB'nin anlamı, Lodash yöntemi kullanılarak hiçbir tip kontrolünün yapılmamasıdır. Örneğin, kişinin nesnelerinin düzgün bir şekilde yazıldığını varsayarsak, z nesnesi b'nin alanı olarak tanımlanmazsa (a && ab && abz) ile bir derleme hatası alırsınız. Ancak _.has (a, 'b.z') kullanıldığında bu hatayı almazsınız. İthalat bildirimine gelince, evet, kesinlikle gerekli.
TiaanM

1
@TiaanM Tam olarak bunu kastettim. Bununla birlikte, türler sorununu kapsayan yeni bir kitaplık buldum: stackoverflow.com/a/55462483/126574
VitalyB

3

Ts-optchain adlı yeni bir kütüphane bu işlevselliği sağlar ve lodash'ın çözümünün aksine türlerinizi de güvende tutar, işte nasıl kullanıldığına dair bir örnek (benioku'dan alınmıştır):

import { oc } from 'ts-optchain';

interface I {
  a?: string;
  b?: {
    d?: string;
  };
  c?: Array<{
    u?: {
      v?: number;
    };
  }>;
  e?: {
    f?: string;
    g?: () => string;
  };
}

const x: I = {
  a: 'hello',
  b: {
    d: 'world',
  },
  c: [{ u: { v: -100 } }, { u: { v: 200 } }, {}, { u: { v: -300 } }],
};

// Here are a few examples of deep object traversal using (a) optional chaining vs
// (b) logic expressions. Each of the following pairs are equivalent in
// result. Note how the benefits of optional chaining accrue with
// the depth and complexity of the traversal.

oc(x).a(); // 'hello'
x.a;

oc(x).b.d(); // 'world'
x.b && x.b.d;

oc(x).c[0].u.v(); // -100
x.c && x.c[0] && x.c[0].u && x.c[0].u.v;

oc(x).c[100].u.v(); // undefined
x.c && x.c[100] && x.c[100].u && x.c[100].u.v;

oc(x).c[100].u.v(1234); // 1234
(x.c && x.c[100] && x.c[100].u && x.c[100].u.v) || 1234;

oc(x).e.f(); // undefined
x.e && x.e.f;

oc(x).e.f('optional default value'); // 'optional default value'
(x.e && x.e.f) || 'optional default value';

// NOTE: working with function value types can be risky. Additional run-time
// checks to verify that object types are functions before invocation are advised!
oc(x).e.g(() => 'Yo Yo')(); // 'Yo Yo'
((x.e && x.e.g) || (() => 'Yo Yo'))();

2

@ Pvl'nin cevabına dayanarak, geçersiz kılmaları kullanırsanız, döndürülen değerinize tür güvenliğini de dahil edebilirsiniz:

function dig<
  T,
  K1 extends keyof T
  >(obj: T, key1: K1): T[K1];

function dig<
  T,
  K1 extends keyof T,
  K2 extends keyof T[K1]
  >(obj: T, key1: K1, key2: K2): T[K1][K2];

function dig<
  T,
  K1 extends keyof T,
  K2 extends keyof T[K1],
  K3 extends keyof T[K1][K2]
  >(obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3];

function dig<
  T,
  K1 extends keyof T,
  K2 extends keyof T[K1],
  K3 extends keyof T[K1][K2],
  K4 extends keyof T[K1][K2][K3]
  >(obj: T, key1: K1, key2: K2, key3: K3, key4: K4): T[K1][K2][K3][K4];

function dig<
  T,
  K1 extends keyof T,
  K2 extends keyof T[K1],
  K3 extends keyof T[K1][K2],
  K4 extends keyof T[K1][K2][K3],
  K5 extends keyof T[K1][K2][K3][K4]
  >(obj: T, key1: K1, key2: K2, key3: K3, key4: K4, key5: K5): T[K1][K2][K3][K4][K5];

function dig<
  T,
  K1 extends keyof T,
  K2 extends keyof T[K1],
  K3 extends keyof T[K1][K2],
  K4 extends keyof T[K1][K2][K3],
  K5 extends keyof T[K1][K2][K3][K4]
  >(obj: T, key1: K1, key2?: K2, key3?: K3, key4?: K4, key5?: K5):
  T[K1] |
  T[K1][K2] |
  T[K1][K2][K3] |
  T[K1][K2][K3][K4] |
  T[K1][K2][K3][K4][K5] {
    let value: any = obj && obj[key1];

    if (key2) {
      value = value && value[key2];
    }

    if (key3) {
      value = value && value[key3];
    }

    if (key4) {
      value = value && value[key4];
    }

    if (key5) {
      value = value && value[key5];
    }

    return value;
}

Oyun alanı örneği .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.