ใน 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;
}

Fu Google ของฉันทำให้ฉันล้มเหลว

เครื่องหมายอัศเจรีย์ของ TS คืออะไรและทำงานอย่างไร

คำตอบ:


691

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

มันอธิบายไว้ที่นี่ :

!ตัวดำเนินการนิพจน์ post-fix ใหม่อาจถูกใช้เพื่อยืนยันว่าตัวถูกดำเนินการนั้นไม่เป็นโมฆะและไม่ได้กำหนดในบริบทที่ตัวตรวจสอบชนิดไม่สามารถสรุปความจริงนั้นได้ โดยเฉพาะการดำเนินการx!สร้างมูลค่าของประเภทxด้วยnullและundefinedยกเว้น คล้ายกับพิมพ์ยืนยันในรูปแบบ<T>xและx as Tการ!ดำเนินการยืนยันไม่ใช่ null จะถูกลบออกเพียงในรหัส JavaScript ที่ปล่อยออกมา

ฉันพบว่าการใช้คำว่า "ยืนยัน" ทำให้เข้าใจผิดเล็กน้อยในคำอธิบายนั้น มันคือ "ยืนยัน" ในแง่ที่ว่าผู้พัฒนายืนยันมันไม่ใช่ในแง่ที่ว่าจะทำการทดสอบ บรรทัดสุดท้ายบ่งชี้ว่าไม่มีการปล่อยโค้ด JavaScript


102
เรียกความกำกวม 'ยืนยัน' ที่ดี
Estus Flask

8
คำอธิบายที่ดี ฉันคิดว่ามันเป็นแนวปฏิบัติที่ดีที่จะทำconsole.assert()กับตัวแปรที่เป็นปัญหาก่อนที่จะผนวก a !หลังจากนั้น เนื่องจากการเพิ่ม!กำลังบอกคอมไพเลอร์ให้เพิกเฉยต่อการตรวจสอบโมฆะดังนั้นจึงเป็นการคอมไพล์เพื่อ noop ในจาวาสคริปต์ ดังนั้นหากคุณไม่แน่ใจว่าตัวแปรนั้นไม่ใช่ค่าว่างควรทำการตรวจสอบยืนยันอย่างชัดเจน
Jayesh

12
เป็นตัวอย่างที่สร้างแรงบันดาลใจ: การใช้ประเภท ES Map ใหม่พร้อมรหัสเช่นdict.has(key) ? dict.get(key) : 'default';คอมไพเลอร์ TS ไม่สามารถอนุมานได้ว่าการgetโทรไม่เคยส่งคืน null / undefined dict.has(key) ? dict.get(key)! : 'default';จำกัด ประเภทให้ถูกต้อง
kitsu.eb

1
มีคำแสลงสำหรับโอเปอเรเตอร์นี้เช่นเดียวกับที่ผู้ดำเนินการ Elvis อ้างถึงโอเปอเรเตอร์ไบนารีหรือไม่
ebakunin

@Jayesh คุณสามารถขยายใน console.assert () การปฏิบัติที่ดีคุณสามารถโพสต์ตัวอย่าง?
Christopher Francisco

168

คำตอบของหลุยส์นั้นยอดเยี่ยม แต่ฉันคิดว่าฉันจะพยายามสรุปให้กระชับ

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


85
จากนั้นในฐานะนักพัฒนาคุณทำให้คุณสับสน
Mike Chamberlain

9
หรือในขณะที่คอมไพเลอร์มันก็เกิดความสับสน หากคอนสตรัคเตอร์ไม่ได้เริ่มต้นคุณสมบัติ แต่ lifecycle hook ทำมันและคอมไพเลอร์ไม่รู้จักสิ่งนี้
Mukus

26
นี่ไม่ใช่ความรับผิดชอบของคอมไพเลอร์ TS แตกต่างจากภาษาอื่น ๆ (เช่น C #), JS (และดังนั้น TS) ไม่ต้องการให้มีการเริ่มต้นตัวแปรก่อนการใช้งาน หรือจะมองมันอีกทางหนึ่งใน JS ตัวแปรทั้งหมดประกาศด้วยvarหรือจะเริ่มต้นโดยปริยายlet undefinedนอกจากนี้คุณสมบัติอินสแตนซ์ของคลาสสามารถประกาศได้เช่นนี้ดังนั้นจึงclass C { constructor() { this.myVar = undefined; } }ถูกกฎหมายอย่างสมบูรณ์ สุดท้ายวงจรชีวิตของตะขอขึ้นอยู่กับกรอบ; เช่น Angular และ React ใช้พวกมันต่างกัน ดังนั้นคอมไพเลอร์ TS จึงไม่สามารถคาดการณ์เหตุผลได้
Mike Chamberlain

1
มีกรณีการใช้งานที่ถูกต้องสำหรับผู้ปฏิบัติงานของปังหรือไม่เมื่อพิจารณาถึงความสุดยอดของการวิเคราะห์ประเภทการควบคุมการไหลใน TS
Eugene Karataev

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