บทนำสู่ TypeScript

เผยแพร่แล้ว: 2020-10-29

ภาษาแรกที่ฉันเคยเขียนโปรแกรมคือ C และ Java ด้วยภาษาเหล่านี้ ทุกครั้งที่คุณกำหนดตัวแปร คุณต้องระบุประเภทของตัวแปร หากคุณพยายามกำหนดค่าประเภทอื่นให้คอมไพเลอร์จะบ่น:

 // main.c int main() { int x = "two"; return 0; } > cc main.c -o main // warning: initialization makes integer from pointer without a cast

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

Gif ของ Sponge Bob ทำให้คอมพิวเตอร์พัง

ต่อมาในอาชีพการงานของฉัน ฉันเริ่มใช้ภาษาการเขียนโปรแกรมบางภาษาโดยไม่มีการพิมพ์ที่รัดกุม เช่น JavaScript หรือ PHP ฉันคิดว่ามันค่อนข้างเจ๋ง: มันง่ายมากที่จะสร้างต้นแบบได้อย่างรวดเร็วโดยไม่ต้องจัดการกับคอมไพเลอร์ที่จู้จี้จุกจิก

ดังที่คุณทราบแล้ว ภาษาการเขียนโปรแกรมที่ใช้ WordPress คือ PHP และ JavaScript ซึ่งหมายความว่าคุณอาจเคยใช้โค้ดปลั๊กอินและธีมของคุณโดยที่คอมไพเลอร์ไม่คอยเฝ้าดูคุณ มีเพียงคุณเท่านั้น ทักษะของคุณ และความคิดสร้างสรรค์ของคุณ และข้อผิดพลาดเช่นนี้:

 const user = getUser( userId ); greet( user.name ); // Uncaught TypeError: user is undefined

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

TypeScript คืออะไร

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

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

TypeScript ทำงานอย่างไร

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

ในฐานะที่เป็น superset ของ JavaScript การเขียนโค้ด TypeScript นั้นง่ายมาก โดยทั่วไปโค้ด JavaScript ต่อไปนี้:

 let user = "David"; let age = 34; let worksAtNelio = true; console.log( user, age, worksAtNelio );

ยังเป็นรหัส TypeScript คุณสามารถคัดลอกและวางใน TypeScript Playground แล้วคุณจะเห็นว่ามันคอมไพล์ แล้วมันพิเศษยังไง? แน่นอนว่าประเภทของมัน ใน JavaScript คุณสามารถทำสิ่งต่อไปนี้:

 let user = "David"; let age = 34; let worksAtNelio = true; user = { name: "Ruth" }; console.log( user, age, worksAtNelio );

แต่จะทำให้เกิดข้อผิดพลาดใน TypeScript เพียงลองใช้ใน Playground แล้วคุณจะเห็นข้อผิดพลาดต่อไปนี้:

 Type '{ name: string; }' is not assignable to type 'string'.

แล้วมันเกี่ยวกับอะไร?

TypeScript สามารถอนุมานชนิดของตัวแปรได้โดยอัตโนมัติ นั่นหมายความว่าเราไม่จำเป็นต้องบอกอย่างชัดเจนว่า “เฮ้ ตัวแปรนี้เป็นสตริง” (หรือตัวเลข หรือบูลีน หรืออะไรก็ตาม); แต่จะพิจารณามูลค่าที่ได้รับในครั้งแรกและอนุมานประเภทตามนั้น

Gif ของผู้ชายที่มีความคิดที่ยอดเยี่ยม

ในตัวอย่างของเรา เมื่อเรากำหนดตัวแปร user เราได้กำหนดสตริงข้อความ "David" ให้กับผู้ใช้ ดังนั้น TypeScript จึงรู้ว่า user เป็น (และควรเป็น) สตริง เสมอ ปัญหาคือหลังจากนั้นเล็กน้อย เราพยายามเปลี่ยนประเภทของตัวแปร user ของเราโดยกำหนดวัตถุที่มีคุณสมบัติเดียว ( name ) ที่มีค่าเป็น สตริง "Ruth" เห็นได้ชัดว่าวัตถุนี้ไม่ใช่ สตริง ดังนั้น TypeScript จึงบ่นและบอกเราว่าไม่สามารถดำเนินการมอบหมายได้

มีสองเส้นทางที่เป็นไปได้ในการแก้ไขปัญหานี้:

 // Option 1 let user = "David"; user = "Ruth"; // OK. // Option 2 let user = { name: "David" }; user = { name: "Ruth" };

ให้ชัดเจนเมื่อกำหนดประเภทของตัวแปรของเรา

แต่การอนุมานประเภทมีไว้เพื่อช่วยเราเท่านั้น หากเราต้องการ เราสามารถบอกชนิดของตัวแปร TypeScript ได้อย่างชัดเจน:

 let user: string = "David"; let age: number = 34; let worksAtNelio: boolean = true; console.log( user, age, worksAtNelio );

ซึ่งแสดงผลลัพธ์เดียวกันกับประเภทที่สรุปโดย TypeScript คำถามนี้ทำให้เกิดคำถาม: ถ้า TypeScript สามารถอนุมานประเภทได้โดยอัตโนมัติ เหตุใดเราจึงต้องการคุณลักษณะนี้

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

พิจารณาตัวอย่างต่อไปนี้:

 const getName = ( person ) => person.name; let david = { name: "David", age: 34 }; console.log( getName( david ) ); // Parameter 'person' implicitly has an 'any' type.

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

 getName( "Hola" ); // Error getName( {} ); // Returns undefined getName( { name: "David" } ); // Returns a string getName( { name: true } ); // Returns a boolean

ทุกครั้งที่เราเรียกใช้ฟังก์ชัน เราได้ผลลัพธ์ที่ต่างออกไป! ดังนั้น แม้ว่า TypeScript จะคอยระวังหลังของเรา แต่เรายังคงพบปัญหา JavaScript แบบคลาสสิก: ฟังก์ชันนี้สามารถรับอะไรก็ได้และสามารถส่งคืนได้ทุกอย่าง

gif ตลก ๆ ของทารกที่เศร้ามาก

คุณเดาเอาเองว่าวิธีแก้ปัญหาคือใส่คำอธิบายประกอบฟังก์ชันให้ชัดเจน

กำหนดประเภทของคุณเอง

แต่ก่อนที่เราจะทำ เรามาดูคุณสมบัติเพิ่มเติมของ TypeScript กันก่อน: ชนิดข้อมูลที่กำหนดเอง จนถึงตอนนี้ ตัวอย่างเกือบทั้งหมดของเราใช้ประเภทพื้นฐาน เช่น string , number , boolean เป็นต้น ซึ่งผมคิดว่าค่อนข้างเข้าใจง่าย เรายังได้เห็นโครงสร้างข้อมูลที่ซับซ้อนมากขึ้น เช่น วัตถุ:

 let david = { name: "David", age: 34 };

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

 let user = "David"; user = { name: "Ruth" }; // Type '{ name: string; }' is not assignable to type 'string'.

ซึ่งบอกเป็นนัยว่าประเภทของวัตถุคือ “ {name:string;} ” ซึ่งคุณสามารถอ่านได้ว่า “นี่คือ วัตถุ ที่มีคุณสมบัติที่เรียกว่า name of type string ” หากนั่นคือวิธีกำหนดประเภทวัตถุ ต่อไปนี้ควรเป็น TypeScript ที่ถูกต้อง:

 let david: { name: string, age: number } = { name: "David", age: 34 };

และมันก็เป็นอย่างนั้นจริงๆ แต่ฉันแน่ใจว่าคุณจะเห็นด้วยว่ามันสะดวก โชคดีที่เราสร้างประเภทใหม่ใน TypeScript ได้

โดยทั่วไป ประเภทที่กำหนดเองนั้นโดยทั่วไปแล้วจะเป็นประเภท TypeScript ปกติที่มีชื่อที่กำหนดเอง:

 type Person = { name: string; age: number; }; let david: Person = { name: "David", age: 34 };

เจ๋งใช่มั้ย ตอนนี้เรามีประเภท บุคคล แล้ว เราสามารถใส่คำอธิบายประกอบฟังก์ชัน getName ของเราได้อย่างง่ายดายและระบุประเภทของพารามิเตอร์อินพุตได้อย่างชัดเจน:

 const getName = ( person: Person ) => person.name;

และการอัปเดตง่ายๆ นี้ทำให้ TypeScript มีข้อมูลมากมาย! ตัวอย่างเช่น ขณะนี้สามารถอนุมานได้ว่าประเภทผลลัพธ์ของฟังก์ชันนี้คือ string เพราะรู้แน่ชัดว่าแอตทริบิวต์ name ของวัตถุ Person นั้นเป็น string เช่นกัน:

 let david: Person = { name: "David", age: 34 }; let davidName = getName( david ); davidName = 2; // Type 'number' is not assignable to type 'string'.

แต่เช่นเคย คุณสามารถใส่คำอธิบายประกอบประเภทผลลัพธ์ได้อย่างชัดเจนหากต้องการ:

 const getName = ( person: Person ): string => person.name;

สิ่งอื่น ๆ ที่มีประเภท TypeScript...

สุดท้ายนี้ ฉันต้องการแบ่งปันความสามารถที่น่าสนใจสองสามอย่างที่คุณสามารถได้รับประโยชน์หากคุณกำหนดประเภทของคุณเองใน TypeScript

TypeScript มีความต้องการอย่างมากในการกำหนดค่าให้กับตัวแปร หากคุณกำหนดประเภทของตัวแปรไว้อย่างชัดเจน คุณสามารถกำหนดค่าที่ตรงกับประเภทนั้นเท่านั้น ลองดูด้วยตัวอย่างหลายประการ:

 type Person = { name: string; age: number; }; let david: Person = { name: "David", age: 34 }; // OK let ruth: Person = { name: "Ruth" }; // Property 'age' is missing in type '{ name: string; }' but required in type 'Person' let toni: Person = { name: "Toni", age: 35, gender: "M" }; // Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'. Object literal may only specify known properties, and 'gender' does not exist in type 'Person'.

ดังที่คุณเห็น ตัวแปร Person จะยอมรับเฉพาะวัตถุที่สอดคล้องกับประเภท Person เท่านั้น หากแอตทริบิวต์บางรายการขาดหายไป ( ruth ไม่มี age ) หรือมีแอตทริบิวต์มากกว่าแอตทริบิวต์ในประเภท ( toni has a gender ) TypeScript จะบ่นและทริกเกอร์ข้อผิดพลาดประเภทที่ไม่ตรงกัน

แต่ถ้าเรากำลังพูดถึงการเรียกใช้ฟังก์ชัน สิ่งต่าง ๆ นั้นแตกต่างออกไป! ประเภทอาร์กิวเมนต์ในฟังก์ชันไม่ใช่ข้อกำหนดที่แน่นอน แต่ระบุอินเทอร์เฟซขั้นต่ำที่พารามิเตอร์ต้องปฏิบัติตาม ฉันรู้ ฉันรู้ นั่นเป็นนามธรรมเกินไป ลองมาดูตัวอย่างต่อไปนี้ ซึ่งฉันเชื่อว่าจะทำให้สิ่งต่างๆ ชัดเจนขึ้น:

 type NamedObject = { name: string; }; const getName = ( obj: NamedObject ): string => obj.name; const ruth = { name: "Ruth" } getName( ruth ); // OK const david = { name: "David", age: 34 }; getName( david ); // OK const toni = { firstName: "Toni" }; getName( toni ); // Argument of type '{ firstName: string; }' is not assignable to parameter of type 'NamedObject'. Property 'name' is missing in type '{ firstName: string; }' but required in type 'NamedObject'.

อย่างที่คุณเห็น เราได้กำหนดฟังก์ชัน getName ใหม่ให้มีลักษณะทั่วไปมากขึ้น: ตอนนี้ใช้อ็อบเจกต์ NamedObject นั่นคือ อ็อบเจ็กต์ที่ต้องมีแอตทริบิวต์ที่เรียกว่า name of type string เมื่อใช้คำจำกัดความนี้ เราจะเห็นว่า ruth และ david ตรงกับข้อกำหนดนี้อย่างไร (ทั้งคู่มีแอตทริบิวต์ name ) แต่ toni ไม่มี เนื่องจากไม่มี name แอตทริบิวต์ที่คาดหวัง

บทสรุป

TypeScript เป็นภาษาการเขียนโปรแกรมที่ขยาย JavaScript โดยการเพิ่มคำจำกัดความของประเภทสแตติก ซึ่งช่วยให้เรากำหนดข้อมูลที่เราทำงานด้วยได้แม่นยำยิ่งขึ้น และที่สำคัญกว่านั้นคือช่วยให้เราตรวจพบข้อผิดพลาดได้ก่อนหน้านี้

ค่าใช้จ่ายในการรวม TypeScript เข้ากับกองการพัฒนานั้นค่อนข้างเล็กและสามารถทำได้ทีละน้อย เนื่องจากโค้ด JavaScript ทั้งหมดเป็นตามคำจำกัดความ TypeScript การเปลี่ยนจาก JavaScript เป็น TypeScript จึงเป็นไปโดยอัตโนมัติ คุณสามารถเพิ่มประเภทและตกแต่งโค้ดของคุณได้ทีละขั้น

หากคุณชอบโพสต์นี้และต้องการทราบข้อมูลเพิ่มเติม โปรดแชร์และแจ้งให้เราทราบในส่วนความคิดเห็นด้านล่าง

ภาพเด่นโดย King's Church International บน Unsplash