typescript สนับสนุนหรือไม่. ผู้ประกอบการ? (และมันเรียกว่าอะไร)


336

typescript ปัจจุบันหรือไม่ (หรือมีแผนจะ) สนับสนุนผู้ประกอบการนำทางที่ปลอดภัยของ?.

เช่น:

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

นอกจากนี้ยังมีชื่อสามัญเพิ่มเติมสำหรับผู้ให้บริการนี้ (มันยากที่ Google จะหายาก)


3
@mattytommo คุณมีสิ่งนั้นใน c # ซึ่งเรียกว่าตัวดำเนินการรวมศูนย์ว่างและใช้เครื่องหมาย ?? syntax weblogs.asp.net/scottgu/archive/2007/09/20/…
basarat

2
@BasaratAli โชคไม่ดีที่การรวมตัวกันเป็นสิ่งที่ดีproperty ?? property2แต่ถ้าคุณลองproperty.company ?? property1.companyและpropertyไม่มีผลคุณจะได้รับNullReferenceException
mattytommo

1
@mattytommo ขอบคุณฉันได้รับทันที '?' ที่จริงแล้วดูดซับการอ้างอิงโมฆะทั้งหมดในห่วงโซ่ หวาน.
basarat

9
@mattytommo สิ่งนี้มีอยู่ในตอนนี้สำหรับ C #: msdn.microsoft.com/en-us/library/dn986595.aspx
Highmastdon

9
ตัวแทน Microsoft ที่เข้าเยี่ยมชมเราเรียกมันว่าผู้ดำเนินการ Elvis เนื่องจากเครื่องหมายคำถามดูเหมือนผมของ Elvis และไมโครโฟนที่เขากำลังร้องเข้า ...
Zymotik

คำตอบ:


167

อัปเดต : รองรับตั้งแต่ TypeScript 3.7 และเรียกว่าการโยงแบบเสริม : https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining

ฉันไม่สามารถหาอ้างอิงถึงมันใด ๆ ในสเปคภาษา typescript

เท่าที่เรียกว่าตัวดำเนินการนี้ใน CoffeeScript จะเรียกว่าตัวดำเนินการที่มีอยู่ (โดยเฉพาะ "ตัวแปร accessor" ของตัวดำเนินการที่มีอยู่)

จากเอกสารของ CoffeeScript เกี่ยวกับผู้ประกอบการ :

ตัวแปร accessor ของตัวดำเนินการที่มีอยู่?.สามารถใช้เพื่อดูดซับการอ้างอิงที่เป็นโมฆะในสายโซ่ของคุณสมบัติ ใช้มันแทนการเข้าถึงจุด.ในกรณีที่ค่าฐานอาจจะเป็นโมฆะหรือไม่ได้กำหนด

ดังนั้นตัวแปรตัวเข้าถึงของตัวดำเนินการที่มีอยู่ดูเหมือนจะเป็นวิธีที่เหมาะสมในการอ้างถึงตัวดำเนินการนี้ และ TypeScript ไม่ปรากฏขึ้นเพื่อรองรับในขณะนี้ (แม้ว่าคนอื่น ๆ ได้แสดงความต้องการฟังก์ชั่นนี้ )


28
"ตัวแปรตัวเข้าถึงของตัวดำเนินการที่มีอยู่" เป็นธรรมชาติ มันเป็นไปไม่ได้ที่จะลืม :) ขอบคุณสำหรับคำตอบอย่างละเอียด
Marty Pitt

1
@MartyPitt แน่นอน! ฉันเห็นด้วยฉันชอบที่จะเห็น) การยอมรับตัวดำเนินการที่กว้างขึ้นเช่นนี้ (C # ได้โปรด!) และ b) ชื่อที่ดีขึ้น (ตัวดำเนินการ "การนำทางที่ปลอดภัย" จากโพสต์บล็อกที่เชื่อมโยงของคุณ
โดนัท

2
Angular นำสิ่งนี้ไปใช้ในแม่แบบ: angular.io/guide/…
Enzoaeneas

5
ในภาษาอื่นบางภาษาเรียกว่าโอเปอเรเตอร์ "Elvis"
k0enf0rNL

4
ประกาศสำหรับ TypeScript 3.7.0 ( github.com/microsoft/TypeScript/issues/
......

146

ไม่ดีเท่าซิงเกิ้ล แต่ทำงานได้:

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

คุณสามารถใช้ && ได้มากเท่าที่คุณต้องการ:

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

33
&& ประเมินตราบใดที่ข้อความนั้นเป็นจริง ถ้าเป็นจริงมันจะคืนค่าสุดท้าย ถ้ามันเป็นเท็จมันจะส่งกลับค่าแรกที่ประเมินว่าเป็นเท็จ นั่นอาจเป็น 0, null, false ฯลฯ || ส่งคืนค่าแรกที่ประเมินค่าเป็นจริง
A. KR

34
ใช้งานไม่ได้หากบาร์ถูกกำหนด แต่ประเมินเป็นเท็จ (เช่นบูลีนเท็จหรือเป็นศูนย์)
mt_serg

96

สิ่งนี้ถูกกำหนดไว้ในข้อมูลจำเพาะเกี่ยวกับการผูกมัดตัวเลือก ECMAScript ดังนั้นเราจึงควรอ้างอิงถึงการผูกมัดตัวเลือกเมื่อเราพูดถึงเรื่องนี้ การติดตั้งที่น่าจะเป็น:

const result = a?.b?.c;

ความยาวและระยะสั้นของทีมนี้คือทีม TypeScript กำลังรอข้อมูลจำเพาะของ ECMAScript ที่จะทำให้รัดกุมขึ้นดังนั้นการนำไปปฏิบัติของพวกเขาอาจไม่ทำลายในอนาคต หากพวกเขานำบางสิ่งไปใช้ในตอนนี้มันจะต้องมีการเปลี่ยนแปลงครั้งใหญ่หาก ECMAScript กำหนดข้อกำหนดใหม่

ดูข้อกำหนดการผูกมัดเสริม

ทีมงาน TypeScript สามารถใช้งานได้ตามที่เห็นสมควร แต่สำหรับการเพิ่ม ECMAScript ในอนาคตพวกเขาต้องการรักษาซีแมนทิกส์แม้ว่าจะให้การเข้าถึงก่อนหน้าเนื่องจากมีฟีเจอร์อื่นมากมาย

ทางลัด

ดังนั้นผู้ให้บริการ Funky ของ 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) ใช้การยืนยันประเภทเพื่อปิดกั้นคอมไพเลอร์
เฟนตัน

สมมติว่าfooเป็นวัตถุที่มีประโยชน์ที่เกิดขึ้นจริง: (foo || {}).barโดยทั่วไปไม่ได้ไปรวบรวมใน typescript เพราะจะไม่เป็นชนิดเดียวกับที่{} fooนั่นเป็นปัญหาที่โซลูชันของ @ VeganHunter มีเป้าหมายเพื่อหลีกเลี่ยง
Simon_Weaver

1
@Simon_Weaver (foo || {bar}). bar จะทำให้คอมไพเลอร์ทำงานได้อย่างราบรื่นและฉันคิดว่า verbosity นั้นเป็นที่ยอมรับ
Harps

@harps จริงนี้เพียงรวบรวมถ้าแถบถูกกำหนดให้เป็นตัวแปรซึ่งมันจะไม่ใหญ่มีแนวโน้มที่จะเป็น
Simon_Weaver

82

อัปเดต: ใช่แล้วรองรับทันที!

มันเพิ่งเปิดตัวด้วย 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

แก้ไข 13 พ.ย. 2019!

ตั้งแต่วันที่ 5 พฤศจิกายน 2019 TypeScript 3.7 ได้จัดส่งแล้วและตอนนี้รองรับ ?.ผู้ให้บริการเครือข่ายเสริม🎉🎉🍾🍾🎉 !!!

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


เพื่อจุดประสงค์ทางประวัติศาสตร์เท่านั้น:

แก้ไข:ฉันได้อัปเดตคำตอบด้วยความคิดเห็นที่ fracz

TypeScript 2.0 เปิดตัว!.มันไม่เหมือนกับ?.(Safe Navigator ใน C #)

ดูคำตอบนี้สำหรับรายละเอียดเพิ่มเติม:

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

สิ่งนี้จะบอกคอมไพเลอร์ว่าค่านั้นไม่เป็นโมฆะหรือไม่ได้กำหนดไว้ นี่จะไม่ตรวจสอบว่าค่าเป็นโมฆะหรือไม่ได้กำหนด

ตัวดำเนินการยืนยัน 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
ไม่เหมือนกัน?เพราะมันอ้างว่ามีการกำหนดค่า ?คาดว่าจะล้มเหลวอย่างเงียบ ๆ / ประเมินว่าเป็นเท็จ อย่างไรก็ตามรู้ดี
fracz

1
ตอนนี้ฉันคิดว่ามัน ... คำตอบนี้ไม่มีจุดหมายเพราะมันไม่ได้ทำ "การนำทางที่ปลอดภัย" ที่ผู้ให้บริการ C # ทำ
Jose

5
นี้ตอบคำถามของฉันแม้ว่า ฉันรู้เกี่ยวกับ จาก c # และลองใน typescript มันใช้งานไม่ได้ แต่ฉันเห็นอย่างนั้น! มีอยู่ แต่ไม่รู้ว่ามันทำอะไร ฉันสงสัยว่ามันเหมือนเดิมหรือไม่ทำการค้นหาด้วยกูเกิ้ลและพบวิธีของฉันในคำถามนี้ซึ่งแจ้งให้ฉันทราบว่าไม่พวกเขาแตกต่างกัน
Llewey

11

Elvis (?.) Chaining Operator ได้รับการสนับสนุนใน TypeScript 3.7

คุณสามารถใช้มันเพื่อตรวจสอบค่า Null: cats?.miowsส่งคืน Null หากแมวเป็น Null หรือไม่ได้กำหนด

คุณยังสามารถใช้มันสำหรับการเรียกใช้เมธอดเสริม: cats.doMiow?.(5)จะเรียกใช้ doMiow หากมีอยู่

cats?.['miows']การเข้าถึงสถานที่ให้บริการยังเป็นไปได้:

การอ้างอิง: https://devblogs.microsoft.com/typescript/announcing-typescript-3-7-beta/


โปรดแก้ไขผม แต่ผู้ประกอบการเอลวิสเป็นอย่างน้อยใน ?:Kotlin คุณมีข้อมูลอ้างอิงหรือไม่?
rekire


1
มันจะได้รับการสนับสนุนใน JS ธรรมดาเร็ว ๆ นี้ - developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Mottie

1
ประกาศการเปิดตัว TypeScript 3.7 กล่าวถึงมัน: devblogs.microsoft.com/typescript/announcing-typescript-3-7
GyörgyBalá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")

มันทำงานได้ดีกับ IntelliSense ใน Visual Studio


1
นี่คือสิ่งที่ฉันกำลังมองหา! มันทำงานได้ใน typescript 2.1.6
Rajab Shakirov

5
หรือคุณสามารถเรียกมันว่าelvis<T>;-)
Simon_Weaver

3
Simon_Weaver ฉันเรียกมันว่า "sad clown": o (
VeganHunter

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 หรือเท็จ

ฉันไม่มีคำอธิบายว่าทำไมพวกเขาจึงไม่คอมไพล์มัน foo == null


3

เราสร้างวิธีการใช้ประโยชน์นี้ในขณะที่ทำงานกับPhonetradrซึ่งสามารถให้การเข้าถึงคุณสมบัติที่ปลอดภัยสำหรับคุณในเชิงลึกด้วย Typescript:

/**
 * 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);


เย็น!! มีคำเตือนใด ๆ หรือไม่? ฉันมีคลาส wrapper ที่มี getters ประมาณ 20 คนให้เขียนพวกเขาทุกคนมี return ประเภทต่อไปนี้ - และทุกฟิลด์ต้องถูกตรวจสอบให้เป็นโมฆะreturn this.entry.fields.featuredImage.fields.file.url;
Drenai

ข้อแม้เพียงอย่างเดียวอาจเป็นผลกระทบต่อประสิทธิภาพการทำงาน แต่ฉันไม่มีคุณสมบัติที่จะพูดกับวิธีที่ JITERS ต่างๆจัดการกับเรื่องนี้
เรย์ Suelzer

2

โดยทั่วไปฉันไม่แนะนำวิธีการนี้ (ระวังปัญหาด้านประสิทธิภาพ) แต่คุณสามารถใช้ตัวดำเนินการสเปรดเพื่อโคลนโคลนวัตถุซึ่งคุณสามารถเข้าถึงคุณสมบัติได้

 const person = { personId: 123, firstName: 'Simon' };
 const firstName = { ...person }.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 infers และนี้จะไม่รวบรวม แต่โดยทั่วไปควรจะทำงาน


2

มันเรียกว่าการผูกมัดเสริมและมันอยู่ในTypescript 3.7

การผูกมัดตัวเลือกช่วยให้เราสามารถเขียนโค้ดที่เราสามารถหยุดการใช้งานนิพจน์บางอย่างทันทีหากเราพบว่าเป็นโมฆะหรือไม่ได้กำหนด


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)

นอกจากนี้คุณยังสามารถไปทางอื่นและใช้สิ่งที่ต้องการ _.get()Lodash' มันกระชับ แต่คอมไพเลอร์จะไม่สามารถตัดสินความถูกต้องของคุณสมบัติที่ใช้:

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

0

ยังไม่ (ณ เดือนกันยายน 2019) แต่เนื่องจาก "ผู้ดำเนินการการนำทางที่ปลอดภัย" อยู่ในขั้นตอนที่ 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 เดือนข้างหน้า แต่หวังว่ามันจะช่วยใครซักคนในเวลาเดียวกัน


-1

_.get(obj, 'address.street.name')ใช้งานได้ดีสำหรับ JavaScript ที่คุณไม่มีประเภท แต่สำหรับ TypeScript เราต้องการตัวดำเนินการ Elvis จริง!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.