มีสี่แง่มุมที่แตกต่างกันใน TypeScript ที่คุณต้องระวัง ประการแรกคำจำกัดความบางประการ:
"วัตถุค้นหา"
ถ้าคุณเขียน enum นี้:
enum Foo { X, Y }
TypeScript จะปล่อยวัตถุต่อไปนี้:
var Foo;
(function (Foo) {
Foo[Foo["X"] = 0] = "X";
Foo[Foo["Y"] = 1] = "Y";
})(Foo || (Foo = {}));
ฉันจะอ้างถึงนี้เป็นวัตถุค้นหา จุดประสงค์ของมันเป็นสองเท่า: เพื่อทำหน้าที่เป็นการทำแผนที่จากสายการตัวเลขเช่นเมื่อเขียนFoo.X
หรือFoo['X']
, และการให้บริการการทำแผนที่จากตัวเลขที่จะสาย ว่าการทำแผนที่กลับจะเป็นประโยชน์สำหรับการแก้จุดบกพร่องหรือเข้าสู่ระบบวัตถุประสงค์ - คุณมักจะมีค่า0
หรือ1
และต้องการที่จะได้รับสตริงที่สอดคล้องกันหรือ"X"
"Y"
"ประกาศ"หรือ " โดยรอบ "
ใน TypeScript คุณสามารถ "ประกาศ" สิ่งที่คอมไพลเลอร์ควรรู้ แต่ไม่สามารถปล่อยโค้ดออกมาได้ สิ่งนี้มีประโยชน์เมื่อคุณมีไลบรารีเช่น jQuery ที่กำหนดอ็อบเจ็กต์บางอย่าง (เช่น$
) ที่คุณต้องการพิมพ์ข้อมูล แต่ไม่ต้องการโค้ดใด ๆ ที่สร้างโดยคอมไพเลอร์ ข้อมูลจำเพาะและเอกสารอื่น ๆ อ้างถึงการประกาศที่ทำในลักษณะนี้ว่าอยู่ในบริบท "แวดล้อม"; สิ่งสำคัญคือต้องทราบว่าการประกาศทั้งหมดใน.d.ts
ไฟล์เป็น "สภาพแวดล้อม" (อาจต้องมีdeclare
ตัวปรับแต่งที่ชัดเจนหรือมีโดยปริยายขึ้นอยู่กับประเภทการประกาศ)
"อินไลน์"
ด้วยเหตุผลด้านประสิทธิภาพและขนาดโค้ดมักจะดีกว่าที่จะมีการอ้างอิงถึงสมาชิก enum แทนที่ด้วยตัวเลขที่เทียบเท่าเมื่อคอมไพล์:
enum Foo { X = 4 }
var y = Foo.X; // emits "var y = 4";
ข้อมูลจำเพาะเรียกสิ่งนี้ว่าการทดแทนฉันจะเรียกมันว่าอินไลน์เพราะมันฟังดูเท่กว่า บางครั้งคุณไม่ต้องการให้สมาชิก enum ถูกอินไลน์เช่นเนื่องจากค่า enum อาจเปลี่ยนแปลงใน API เวอร์ชันอนาคต
Enums ทำงานอย่างไร?
ลองแบ่งมันออกตามลักษณะของ enum แต่ละด้าน น่าเสียดายที่แต่ละส่วนในสี่ส่วนนี้จะอ้างอิงคำศัพท์จากส่วนอื่น ๆ ทั้งหมดดังนั้นคุณอาจต้องอ่านข้อมูลทั้งหมดนี้มากกว่าหนึ่งครั้ง
คำนวณเทียบกับไม่คำนวณ (ค่าคงที่)
สมาชิก Enum สามารถคำนวณหรือไม่ก็ได้ ข้อมูลจำเพาะเรียกร้องที่ไม่ได้คำนวณสมาชิกอย่างต่อเนื่องแต่ฉันจะเรียกพวกเขาไม่ใช่การคำนวณความสับสนหลีกเลี่ยงกับconst
คำนวณสมาชิก enum เป็นหนึ่งที่มีค่าไม่เป็นที่รู้จักที่รวบรวมเวลา แน่นอนว่าการอ้างอิงถึงสมาชิกที่คำนวณแล้วไม่สามารถอินไลน์ได้ ตรงกันข้ามไม่ใช่คำนวณสมาชิก enum เป็นครั้งเดียวที่มีค่าเป็นที่รู้จักกันที่รวบรวมเวลา การอ้างอิงถึงสมาชิกที่ไม่ได้คำนวณจะถูกอินไลน์เสมอ
สมาชิก enum ใดที่คำนวณและไม่ได้คำนวณ ประการแรกสมาชิกทั้งหมดของconst
enum เป็นค่าคงที่ (เช่นไม่คำนวณ) ตามที่ชื่อมีความหมาย สำหรับ enum ที่ไม่ใช่เงื่อนไขนั้นขึ้นอยู่กับว่าคุณกำลังดูenum (ประกาศ) โดยรอบหรือ enum ที่ไม่ใช่สภาพแวดล้อม
สมาชิกของdeclare enum
(เช่น enum โดยรอบ) เป็นค่าคงที่ถ้ามีตัวเริ่มต้นเท่านั้น มิฉะนั้นจะคำนวณ โปรดทราบว่าใน a declare enum
อนุญาตให้ใช้ตัวเริ่มต้นที่เป็นตัวเลขเท่านั้น ตัวอย่าง:
declare enum Foo {
X, // Computed
Y = 2, // Non-computed
Z, // Computed! Not 3! Careful!
Q = 1 + 1 // Error
}
สุดท้ายสมาชิกของ enums ที่ไม่ประกาศ non-const จะถูกพิจารณาให้คำนวณเสมอ อย่างไรก็ตามนิพจน์การเริ่มต้นจะลดลงเหลือเพียงค่าคงที่หากคำนวณได้ในเวลาคอมไพล์ ซึ่งหมายความว่าสมาชิก enum ที่ไม่ใช่ const จะไม่อยู่ในบรรทัด (พฤติกรรมนี้เปลี่ยนไปใน TypeScript 1.5 โปรดดู "การเปลี่ยนแปลงใน TypeScript" ที่ด้านล่าง)
const เทียบกับ non-const
const
การประกาศ enum สามารถมีconst
ตัวปรับเปลี่ยนได้ ถ้าเป็น enum const
, ทั้งหมดอ้างอิงถึงสมาชิก inlined
const enum Foo { A = 4 }
var x = Foo.A; // emitted as "var x = 4;", always
const enums ไม่สร้างอ็อบเจ็กต์การค้นหาเมื่อคอมไพล์ ด้วยเหตุนี้การอ้างอิงFoo
ในโค้ดด้านบนจึงเป็นข้อผิดพลาดยกเว้นเป็นส่วนหนึ่งของการอ้างอิงสมาชิก จะไม่มีFoo
วัตถุอยู่ในรันไทม์
ไม่ใช่ const
หากการประกาศ enum ไม่มีโมดิconst
ฟายเออร์การอ้างอิงถึงสมาชิกจะถูกแทรกในกรณีที่สมาชิกไม่ได้คำนวณเท่านั้น enum ที่ไม่ใช่ const และไม่ประกาศจะสร้างอ็อบเจ็กต์การค้นหา
ประกาศ (โดยรอบ) เทียบกับไม่ประกาศ
คำนำที่สำคัญคือว่าdeclare
ใน typescript มีความหมายที่เฉพาะเจาะจงมาก: วัตถุนี้มีอยู่ที่อื่น ใช้สำหรับอธิบายวัตถุที่มีอยู่ การใช้declare
กำหนดวัตถุที่ไม่มีอยู่จริงอาจส่งผลเสียได้ เราจะสำรวจสิ่งเหล่านั้นในภายหลัง
ประกาศ
declare enum
จะไม่ปล่อยวัตถุการค้นหา การอ้างอิงถึงสมาชิกจะมีการขีดเส้นใต้หากสมาชิกเหล่านั้นคำนวณ (ดูด้านบนเกี่ยวกับการคำนวณเทียบกับไม่คำนวณ)
มันเป็นสิ่งสำคัญที่จะทราบว่ารูปแบบอื่น ๆ ของการอ้างอิงถึงdeclare enum
จะได้รับอนุญาตเช่นรหัสนี้ไม่ได้รวบรวมข้อผิดพลาด แต่จะล้มเหลวที่รันไทม์:
// Note: Assume no other file has actually created a Foo var at runtime
declare enum Foo { Bar }
var s = 'Bar';
var b = Foo[s]; // Fails
ข้อผิดพลาดนี้อยู่ในหมวดหมู่ "อย่าโกหกคอมไพเลอร์" หากคุณไม่มีวัตถุชื่อFoo
รันไทม์อย่าเขียนdeclare enum Foo
!
A declare const enum
ไม่แตกต่างจาก a const enum
ยกเว้นในกรณีของ --preserveConstEnums (ดูด้านล่าง)
ไม่ใช่ประกาศ
enum const
ไม่ใช่ประกาศผลิตวัตถุค้นหาถ้ามันไม่ได้ Inlining ได้อธิบายไว้ข้างต้น
ธง --preserveConstEnums
แฟล็กนี้มีเอฟเฟกต์เดียวเท่านั้น: enums ที่ไม่ประกาศจะส่งอ็อบเจ็กต์การค้นหา การซับในไม่ได้รับผลกระทบ สิ่งนี้มีประโยชน์สำหรับการดีบัก
ข้อผิดพลาดทั่วไป
ข้อผิดพลาดที่พบบ่อยที่สุดคือการใช้declare enum
เมื่อเป็นประจำenum
หรือconst enum
จะเหมาะสมกว่า รูปแบบทั่วไปคือ:
module MyModule {
// Claiming this enum exists with 'declare', but it doesn't...
export declare enum Lies {
Foo = 0,
Bar = 1
}
var x = Lies.Foo; // Depend on inlining
}
module SomeOtherCode {
// x ends up as 'undefined' at runtime
import x = MyModule.Lies;
// Try to use lookup object, which ought to exist
// runtime error, canot read property 0 of undefined
console.log(x[x.Foo]);
}
อย่าลืมกฎทอง: ไม่เคยdeclare
สิ่งที่ไม่ได้มีอยู่จริง ใช้const enum
ถ้าคุณต้องการซับในเสมอหรือenum
ถ้าคุณต้องการให้วัตถุค้นหา
การเปลี่ยนแปลงใน TypeScript
ระหว่าง TypeScript 1.4 และ 1.5 มีการเปลี่ยนแปลงในลักษณะการทำงาน (ดูhttps://github.com/Microsoft/TypeScript/issues/2183 ) เพื่อให้สมาชิกทั้งหมดของ enums ที่ไม่ได้ประกาศ non-const ได้รับการปฏิบัติตามที่คำนวณได้แม้ว่า พวกเขาเริ่มต้นอย่างชัดเจนด้วยตัวอักษร "การแยกทารกออกจากกัน" นี้เพื่อที่จะพูดทำให้พฤติกรรมการฝังในสามารถคาดเดาได้ง่ายขึ้นและแยกแนวคิดของแนวคิดconst enum
ปกติออกจากปกติenum
ได้อย่างหมดจด ก่อนที่จะมีการเปลี่ยนแปลงนี้สมาชิกที่ไม่ได้คำนวณของ enums ที่ไม่ได้คำนวณได้ถูกแทรกซึมในเชิงรุกมากขึ้น