1 คะแนน โดย GN⁺ 2023-07-09 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • รวบรวมรายการโปรเจกต์ Type Mapping, Code Generation / External Tool และ Adapter ที่เสนอความจำเป็นให้ TypeScript ปล่อย ข้อมูลชนิดตอนรันไทม์ และใช้เป็นทางเลี่ยงปัญหานี้
  • ปัญหาหลักคือเมื่อจัดการ serialization และ validation โดยไม่มีระบบชนิดแบบ reflective จะต้องมี boilerplate ไม่รู้จบ หรือการสร้างโค้ด bespoke โดยอิงไฟล์ schema
  • ทางเลี่ยงที่ยกมามี io-ts, zod เป็นต้น แต่มีความไม่สะดวกตรงที่ต้องประกาศชนิดซ้ำในรูปแบบเฉพาะของแต่ละไลบรารี และไลบรารีเหล่านี้ไม่สามารถรองรับความสามารถด้านชนิดทั้งหมดของ TypeScript ได้
  • แม้ยอมรับว่าการลบชนิดของ TypeScript มีข้อดีคือทำให้โปรเจกต์ JavaScript สามารถใช้ JavaScript ที่ถูกปล่อยออกมาได้โดยไม่ต้องมีความรู้เกี่ยวกับ TypeScript แต่ก็โต้แย้งว่าสามารถปล่อยข้อมูลชนิดในรูปแบบตาราง lookup ที่แยกจากโค้ดได้
  • ขอร้องว่าอย่าแก้ด้วย decorator พร้อมเสนอแนวทางอย่าง higher-order function ที่คอมไพเลอร์รู้จัก เช่น typescript.generateRuntimeType<T>() เพื่อรองรับการใช้ interface และชนิดจากไลบรารีภายนอก รวมถึงแนวทางแบบ F# Type Providers และ C# Source Generators
  • เชื่อมโยงไปยังการพูดคุยเดิมที่เกี่ยวข้องอย่าง GitHub issue อายุ 8 ปี และขอให้โปรเจกต์ที่เจอปัญหาเดียวกันส่ง PR เพื่อเพิ่มเข้ารายการ โดยไม่ขึ้นกับจำนวนดาว

1 ความคิดเห็น

 
GN⁺ 2023-07-09
ความเห็นจาก Hacker News
  • ต้องอ่านคำอธิบายปัญหาราว 4 รอบกว่าจะเข้าใจว่าต้องการอะไร และกรณีนี้ก็แสดงให้เห็นชัดว่าทำไม การเขียนให้กระชับ จึงสำคัญ
    สิ่งที่ต้องการจริง ๆ ดูเหมือนจะเป็นความปลอดภัยของชนิดข้อมูลตอนรันไทม์ แต่ TypeScript วางเส้นมาตลอดว่าจะไม่กลายเป็นรันไทม์ทดแทน/ส่วนขยายของ JavaScript จึงดูมีโอกาสน้อย TypeScript มีหน้าที่คอมไพล์เป็น JS แล้วก็จบ ส่วนสิ่งที่เกิดขึ้นต่อจากนั้นใน V8 เป็นต้นไปอยู่นอกขอบเขต
    ข้อเรียกร้องว่า “TypeScript ควรปล่อยข้อมูลชนิดข้อมูลสำหรับรันไทม์ออกมา” จึงใกล้เคียงกับการขอ ผลิตภัณฑ์ใหม่ ที่ค่อนข้างต่างจาก TypeScript ปัจจุบัน

    • ดูจะไม่ใช่การเรียกร้องความปลอดภัยของชนิดข้อมูลตอนรันไทม์โดยตรง แต่ใกล้เคียงกับการขอให้สะท้อนชนิดข้อมูลตอนคอมไพล์เพื่อสร้างค่า แล้วนำข้อมูลนั้นไปใช้ตอนรันไทม์มากกว่า
      เช่น อยากมีฟังก์ชัน validate แบบทั่วไปที่รับอินเทอร์เฟซตามอำเภอใจและอ็อบเจ็กต์มาเพื่อตรวจสอบได้ อาจทำได้โดยใช้การสะท้อนตอนคอมไพล์เพื่อสร้างโค้ดตรวจสอบ JS ตามชนิดข้อมูล หรือส่ง T เป็นอาร์กิวเมนต์ตอนรันไทม์เพื่อนำมาเปรียบเทียบ แต่ภายใต้ปรัชญาปัจจุบันของ TypeScript ก็ดูยาก
    • แค่อ่านหัวข้อก็รู้ทันทีว่านี่คือฟีเจอร์ที่อยากได้มานาน และใกล้เคียงกับการขอ https://github.com/rbuckton/reflect-metadata ที่เป็นทางการและรองรับดีกว่า
      TypeScript รู้ข้อมูลชนิดข้อมูลจำนวนมากระหว่างคอมไพล์ แต่เมื่อคอมไพล์เสร็จก็ทิ้งข้อมูลนั้นไป ทั้งที่สามารถส่งออกข้อมูลนี้เป็นไฟล์หรือเก็บไว้เป็นเมตะดาต้าใน Reflect ได้ การทิ้งมันไปจึงเป็นข้อจำกัดที่น่าเสียดาย โดยเฉพาะเมื่อคิดถึงธรรมชาติของ JavaScript ที่ว่า “ทุกอย่างคืออ็อบเจ็กต์”
      ถึงจะยังไม่ได้ความปลอดภัยของชนิดข้อมูลแบบ TS ที่แม่นยำครบถ้วน ก็ยังสามารถเสริมการตรวจสอบบางส่วนด้วย getter/setter หรือ ES2015 Proxy ได้ และถ้าเก็บข้อมูลชนิดข้อมูลไว้ตอนรันไทม์ ก็จะเปิดทางให้ความเป็นไปได้ที่น่าสนใจหลายอย่างในฐานะ เมตะดาต้าที่รันได้
    • ที่บอกว่าทำไม่ได้เพราะ TypeScript เป็นคอมไพเลอร์นั้นไม่ถูกนัก แค่รองรับไลบรารี reflection ที่ส่งออกข้อมูลชนิดข้อมูลเป็น อ็อบเจ็กต์ JS และเปิดให้ค้นดูได้ตอนรันไทม์ก็พอ
      ตอนนี้ TS enum ก็ถูกส่งออกเป็นอ็อบเจ็กต์ JS อยู่แล้ว จึงตรวจดูได้ตอนรันไทม์ แต่ยูเนียนชนิดข้อมูลแบบสตริงลิเทอรัลไม่เป็นเช่นนั้น นอกจากนี้ TypeScript ยังรองรับ ฟังก์ชัน type guard ดังนั้นการใช้ข้อมูลในระบบชนิดข้อมูลมา生成ฟังก์ชันแบบนี้อัตโนมัติก็ดูไม่ใช่เรื่องยากมาก
    • แค่อ่านผ่าน ๆ ก็เห็นว่าความต้องการชัดเจนอยู่แล้ว คืออยากให้ TypeScript ส่งออกข้อมูลชนิดข้อมูลที่ค้นพบระหว่างกระบวนการลบชนิดข้อมูลไปยัง ช่องทางเสริม ที่อยู่ข้าง ๆ JavaScript ที่ถูกสร้างขึ้น
      นึกเทียบกับไฟล์ PDB ก็ได้ TypeScript มีข้อมูลนี้อยู่แล้วก่อนจะทิ้งไป จึงไม่ถึงกับต้องมีผลิตภัณฑ์ใหม่ทั้งหมด
    • ด้านบนของ README มีลิงก์ไปยัง “GitHub issue อายุ 7 ปี” และที่นั่นอธิบายปัญหาไว้อย่างตรงไปตรงมามากกว่า
      https://github.com/microsoft/TypeScript/issues/3628
  • จากมุมมองของ PM ของ TypeScript ก็เข้าใจความต้องการนี้ เพราะการตรวจสอบข้อมูลมักต้องใช้ การตรวจชนิดข้อมูลตอนรันไทม์ และก็มีไลบรารีจำนวนมากที่พยายามมาอุดช่องว่างนี้
    แต่การที่มีไลบรารีจำนวนมากซึ่งตัดสินใจออกแบบต่างกันเอง ก็เป็นสัญญาณว่าปัญหานี้ไม่ใช่ปัญหาที่มีคำตอบชัดเจนเพียงหนึ่งเดียวและถูกแก้ไปแล้ว TypeScript รู้เรื่องนี้ตั้งแต่ช่วงออกแบบแรกเริ่ม และผมคิดว่าหลักการนี้ก็ยืนหยัดมาได้ดี
    ในทางกลับกัน TypeScript ก็ทรงพลังพอที่จะอธิบายด้วยชนิดข้อมูลได้อย่างแม่นยำว่าสิ่งที่ไลบรารีตรวจชนิดข้อมูลตอนรันไทม์ทำจริง ๆ คืออะไร และผู้ใช้ก็สามารถประกอบตรรกะการตรวจสอบตอนรันไทม์จากชนิดข้อมูลผ่าน API ได้ ระดับนี้ให้ความยืดหยุ่นที่สมเหตุสมผลแล้ว

    • เพิ่งเริ่มเขียนโปรแกรมได้ไม่นาน เลยสงสัยว่าทำไม TypeScript ถึงสร้าง ชนิดข้อมูลที่ผู้ใช้กำหนดเอง ที่ซับซ้อนกว่านี้ไม่ได้
      เช่น นิยามตัวเลขในช่วงที่กำหนด หรือสตริงที่ตรงกับรูปแบบรหัสไปรษณีย์เป็นชนิดข้อมูล แล้วให้คอมไพเลอร์ใช้ฟังก์ชันตรวจสอบที่เขียนเหมือนฟังก์ชันทั่วไปเพื่อตรวจความถูกต้อง จึงนึกภาพถึงภาษาที่ทำแบบนั้นได้ เลยสงสัยว่าการไม่มีฟีเจอร์นี้เป็นการตัดสินใจเชิงออกแบบเพื่อหลีกเลี่ยงความซับซ้อนที่ไม่จำเป็น หรือมีข้อจำกัดทางเทคนิคอย่างเรื่องประสิทธิภาพ
    • ถ้า TypeScript compiler มี ปลั๊กอินพรีโปรเซสเซอร์ อย่างเป็นทางการก็น่าจะช่วยได้
      ตอนนี้คนที่ต้องการก็สามารถทำพรีโปรเซสเซอร์ที่สร้างอ็อบเจ็กต์รันไทม์จากข้อมูลชนิดข้อมูลก่อนส่งให้ tsc ได้อยู่แล้ว แต่ความพยายามเหล่านี้กระจัดกระจายกันไป ถ้ามีพรีโปรเซสเซอร์แบบปลั๊กอินอย่างเป็นทางการและมี ecosystem รองรับ ก็อาจค้นพบทางออกที่ดีได้
    • ถ้า Microsoft โฮสต์ MacroScript ในรูปแบบปลั๊กอิน TypeScript หรือ top-level wrapper ก็อาจแก้ปัญหานี้ได้
      แค่จุดประกายขึ้นมา ชุมชนก็น่าจะช่วยกันดูแลรักษาต่อ และสามารถตอบโจทย์ความต้องการด้านการสร้างโค้ดได้หลายแบบ ตั้งแต่การสร้างไคลเอนต์ไปจนถึงการยืนยันชนิดข้อมูลตอนรันไทม์
    • อยากรู้ว่าคิดอย่างไรกับข้อเสนออีกแบบที่ให้เก็บข้อมูลชนิดข้อมูลไว้ในอ็อบเจ็กต์ Class หลังคอมไพล์
  • การไม่ทำแบบนี้มีเหตุผลอยู่ ถ้า TypeScript ทำเช่นนั้น มันก็จะกลายเป็นเหมือน runtime ที่คร่อมอยู่บน JavaScript และกลายเป็นภาษาใหม่ที่คอมไพล์ไปเป็น JS
    ตอนนี้ TypeScript ยังใกล้เคียงกับ JavaScript ที่มี type annotation อยู่แล้ว ภาษาที่คอมไพล์ไปเป็น JS ก็มีเยอะมาก ดังนั้นก็ไปใช้ตัวใดตัวหนึ่งได้ คนที่ต้องการ TypeScript ที่มี runtime type ดูเหมือนจะอยากเขียนโค้ดสไตล์ Java/OOP มากกว่า JavaScript แต่ JavaScript เป็นภาษา dynamic type และนั่นก็เป็นข้อดีอย่างหนึ่งด้วย

    • ไม่จำเป็นต้องเป็นแบบนั้นเสมอไป ถ้าเป็น macro อย่าง generateTypeInfo!() ที่ขยายออกเป็นอ็อบเจ็กต์ JS ซึ่งเข้ารหัส type Foo ก็ยังคอมไพล์เป็น JavaScript ที่อ่านรู้เรื่องได้อยู่
      เพียงแต่ macro นี้จะทำลายคุณสมบัติที่ว่า “TS = JS ที่มี type annotation และการคอมไพล์คือแค่ลบ annotation ออก” เพราะต้องคำนวณ structural type ของ Foo จริง ๆ
      อีกทั้ง type ของ TypeScript เป็นแบบ structural ดังนั้นถ้าตรวจโครงสร้างอ็อบเจ็กต์ที่ runtime ก็พอจะรู้ type ได้ระดับหนึ่ง แต่ถ้าต้องการข้อมูลเชิงนามธรรมที่ถูกลบหายไป เช่น การเป็นสมาชิกของ string union หรือชื่อ type ก็จะกลายเป็นปัญหาที่ยากอย่างรวดเร็ว เพราะ type system ของ TypeScript เป็นแบบทัวริงสมบูรณ์และมีการแปลงโครงสร้างโดยนัย
    • มีทั้งสองอย่างพร้อมกันได้ ลองนึกถึงโลกแบบ homoiconicity ของ Lisp ที่โค้ดกับข้อมูลอยู่ใกล้กัน
      เอาจริง ๆ ต่อให้ไม่มี runtime ถ้าเปิดเผย type ออกมาในรูปข้อมูลก็ทำ reflection ได้อยู่แล้ว พอเห็นนักพัฒนาจำนวนมากพยายามเลียนแบบสิ่งนี้ ก็เลยรู้สึกว่าการไม่ทำเสียอีกที่ดูไม่ฉลาด
    • ผมไม่ค่อยเข้าใจตรรกะนั้น TypeScript เป็นภาษา superset ใหม่ที่คอมไพล์ไปเป็น JS อยู่แล้ว
      runtime type เป็นการแก้ปัญหาที่ต้องดูแล type system สำหรับคอมไพล์และ type system สำหรับตรวจสอบข้อมูลแยกกันแบบซ้ำซ้อน ซึ่งดูไม่เกี่ยวกับ OOP โดยตรงนัก io-ts ที่นิยมใช้เป็นทางอ้อมแก้ปัญหานี้ก็เอนไปทาง functional programming อย่างมาก
    • สำหรับโปรเจ็กต์ที่ใหญ่พอ ภาษา dynamic type ไม่ค่อยดีนัก และแทบจะเหมือนหายนะที่พัวพันกันจากการสะดุดค่าคาดไม่ถึงอย่าง NaN อยู่เรื่อย ๆ
      จาก workflow ที่ผมเห็น TypeScript เป็นภาษาที่คอมไพล์ไปเป็น JS อยู่แล้ว ดังนั้นในเมื่อเป็นแบบนั้น ก็ควรใช้ข้อได้เปรียบนั้นให้เต็มที่
    • TypeScript ไม่ได้เป็นแค่ JavaScript ที่มี type annotation อย่างเดียว มันอาจเป็นแบบนั้นได้ในบางโหมด แต่ก็มีความสามารถในการปล่อยโค้ดเป็นโครงสร้าง JS แบบเก่าด้วย จนหน้าตาต่างจากโค้ด TS ต้นฉบับโดยสิ้นเชิง
      มันใกล้กับ type annotation + Babel มากกว่า และการเพิ่มฟีเจอร์ที่มีประโยชน์มากอีกอย่างก็ดูเป็นความคิดที่ดี การที่ไม่สามารถแปลงสตริงที่ JSON.parse แล้วให้เป็นโครงสร้างที่มี type อย่างปลอดภัยได้เป็นเรื่องแปลก และภาษาอื่น ๆ ส่วนใหญ่ทำได้
  • ก่อนจะพูดถึงข้างล่าง นี่เป็นประเด็นที่ถกกันได้อย่างสมเหตุสมผล และผมไม่คิดว่ามีคำตอบที่ถูกต้องเพียงหนึ่งเดียวอย่างเป็นกลาง
    TypeScript เป็นชั้นทางเลือกบน JavaScript ยกเว้นกรณี Enum ที่ปล่อยอ็อบเจ็กต์ออกมาด้วย และโค้ด TS ก็กลายเป็น JS ได้ด้วยการลบ type ออกเท่านั้น ถ้าไม่ออกนอกหลักการนี้มากเกินไป สิ่งที่ต้องการจริง ๆ ก็ดูคล้ายไลบรารีที่สร้าง serializer/validator จาก type มากกว่า ไลบรารีแบบนั้นมีอยู่แล้วจำนวนมาก และสุดท้ายก็ดูเหมือนเป็นการเรียกร้องให้เลือกหนึ่งในนั้นมาเป็นตัวเลือกมาตรฐานอย่างเป็นทางการ
    โดยส่วนตัวผมไม่อยากให้ภาษาแกนของ TypeScript มี runtime reflection เข้ามา ที่ runtime ควรมีแค่ JavaScript ล้วน ๆ และผมให้คุณค่ากับการที่ JS ที่ปล่อยออกมายังอ่านง่ายและดีบักได้ แม้ไม่มี source map

    • นอกจาก Enum แล้วก็ยังมีข้อยกเว้นอื่นที่ปล่อยโค้ด runtime ออกมาเช่นกัน และส่วนใหญ่ก็มักถูกมองว่าเป็นความผิดพลาดเหมือนกัน ตัวอย่างเด่นคือ module และ namespace ที่เคยเป็นโครงสร้าง runtime
      อย่างไรก็ตาม ช่วงหลายปีที่ผ่านมา ส่วนใหญ่ถูกใช้ภายใน TypeScript เอง และ TypeScript ก็เริ่มถอยออกจากตรงนั้นในช่วงหลัง อีกข้อยกเว้นที่ค่อนข้างได้รับความนิยมคือ parameter properties ซึ่งเป็นไวยากรณ์สำหรับกำหนด type ของสมาชิกคลาสจากพารามิเตอร์ของ constructor และดูเหมือนจะหลบเสียงคัดค้านได้มากกว่าเพราะช่วยลด boilerplate ที่ซ้ำซ้อน
    • คำพูดที่ว่า “โค้ด TS กลายเป็น JS ได้โดยไม่ต้องแปลงอะไร” จะพูดได้ก็ต่อเมื่อมองทั้งภาษาและการเขียน type แบบนั้น
  • แทนที่จะไปอ้อนวอนเทพ TypeScript อาจจะดีกว่าถ้าไปทำข้อตกลงกับฝั่ง JavaScript เพื่อเอา type checking เข้าไปใน JS
    ถ้าต้องการ runtime type ก็ใช้ type guard และถ้าต้องใช้บ่อยก็ใช้ io-ts หรือ zod เพื่อเขียน type ให้เป็น validator/codec/schema จนกว่าจะมีแนวทางที่ JavaScript เห็นพ้องร่วมกัน ผมไม่คิดว่า TS spec, ตัว type checker และชุมชนควรต้องแบกรับเรื่อง runtime validation

    • แม้จะไม่สมบูรณ์แบบ แต่ผมค่อนข้างพอใจกับวิธีใช้ schema และ validator ด้วย zod แล้ว derive type จากตรงนั้น ชอบตรงที่ validation พัฒนาไปพร้อมกับการเปลี่ยนแปลงของ type
      บางคนอาจรู้สึกว่าฟีเจอร์แบบนี้ควรอยู่ในภาษา แต่แต่ละไลบรารีก็มีตัวเลือกด้านการออกแบบที่ชวนถกเถียงเยอะ ดังนั้นการเลือก implementation ให้ตรงกับความต้องการของโปรเจ็กต์อาจจะดีกว่า
      อย่างไรก็ตาม แพตเทิร์นอย่าง runtime validation ของ zod และการ derive type จาก schema ไม่ได้เข้ากับ pattern matching ของ ts-pattern ได้อย่างราบรื่นเสมอไป type definition ของไลบรารีพวกนี้ซับซ้อนราวกับเขาวงกต และบางครั้งโค้ดที่ดูเหมือนควรใช้ได้ก็ไม่ทำงานอย่างที่คาด ถ้าเอา runtime safety กับ exhaustiveness-checked pattern matching มารวมกันได้อย่างลื่นไหลก็น่าจะดีมาก
    • ยังน่าสงสัยอย่างมากว่า JavaScript ควรมีฟีเจอร์แบบนั้นจริงหรือไม่
      ผมรู้สึกว่าทิศทางการออกแบบของ Promise, decorator และ pipe operator แบบใหม่ ล้วนเดินมาผิดทาง
  • จำได้ว่าหนึ่งในนักพัฒนา TypeScript เคยบอกว่า ถ้าเริ่มใหม่ได้อีกครั้งคงจะไม่ใส่ enum เข้ามา เพราะ enum เป็นฟีเจอร์เดียวที่ปล่อยโค้ด runtime ออกมา
    TypeScript ไม่เปลี่ยนพฤติกรรม runtime และก็ไม่มีอะไรเฉพาะของ TypeScript อย่าง {#if} ด้วย

    • คำนั้นเป็นความจริง ผมไม่แน่ใจว่าเหตุผลนั้นเป็นเหตุผลเดียวหรือไม่ แต่แน่นอนว่าเป็นหนึ่งในเหตุผล
      อย่างไรก็ตาม TypeScript ก็มีฟีเจอร์อยู่หลายอย่างที่ไปไกลกว่า “JavaScript + type annotation” เช่น namespace, ไวยากรณ์กำหนด property ของคลาสจากอาร์กิวเมนต์ constructor, experimental decorator แบบเก่า และไวยากรณ์พารามิเตอร์ this ที่หายไปตอนคอมไพล์ แต่ก็ดูเหมือนมากกว่าแค่การใส่ type annotation ให้ฟังก์ชัน JS
  • ชื่อที่ดีกว่าน่าจะเป็น “TypeScript โปรดให้ reflection/runtime type มาเถอะ” มากกว่า
    ตอนนี้วิธีแก้ที่ดีที่สุดน่าจะเป็น emitDecoratorMetadata: https://www.typescriptlang.org/tsconfig#emitDecoratorMetadata

  • ผู้เขียนดูเหมือนจะเข้าใจเป้าหมายการออกแบบของ TypeScript ผิดไป เป้าหมายไม่ใช่การสร้างผลลัพธ์ JS ที่สะอาดโดยไม่มีความซับซ้อน แต่คือการทำให้ ความหมายเชิงรันไทม์ ของ TypeScript คงเหมือนกับ JavaScript
    ยกเว้นกรณี enum ซึ่งเป็นข้อยกเว้นที่น่าเสียดาย TypeScript จะกลายเป็น JavaScript ได้เพียงแค่ลบ type annotation ออก ระบบนิเวศรอบ ๆ TypeScript พึ่งพาการลบ type ออกทั้งหมดอยู่แล้ว และถ้าความต้องการนี้ถูกรับไปใช้ การรองรับ TS ของ ESBuild, Deno และ Bun อาจแทบเป็นไปไม่ได้ เพราะแต่ละตัวจะต้องนำ tsc ทั้งก้อนมาเขียนใหม่ด้วยภาษาของตัวเอง
    ในทางกลับกัน ไลบรารีที่ซับซ้อนซึ่ง OP บ่นถึงนั้นถูกทำไว้ใน user space จึงเข้ากันได้กับเครื่องมือเหล่านี้

    • มีหลายวิธีที่จะส่งออก ข้อมูลชนิดรันไทม์ แบบสแตติกได้โดยไม่ต้องมีรันไทม์ เช่น แปลง keyof เป็นรายการคีย์ของคลาส หรือทำให้คลาส TS กำหนดค่าเริ่มต้นของคีย์เป็น undefined แบบเดียวกับคลาส ES6
      ตอนนี้คลาส TypeScript จะลบคีย์ทั้งหมดที่ไม่ได้กำหนดไว้อย่างชัดเจนออก ทำให้เมื่อเรียก Object.keys() กับอินสแตนซ์ใหม่จะไม่เห็นอะไรเลย แค่มีตัวเลือกใน tsconfig.json ให้แปลงคลาส TS ให้เหมือนคลาส ES6 หรืออนุญาตให้ใช้คลาส ES6 ควบคู่ใน TS ได้ ก็จะทำให้การสร้างโค้ดง่ายขึ้นมากแล้ว
      นอกจากนี้ ถ้ามีไวยากรณ์แบบสแตติกอย่าง tstypeof Foo::bar ที่คอมไพล์เป็น "string" ได้ก็คงยอดเยี่ยม แค่มี RTTI พื้นฐานก็อาจทำให้โค้ด TypeScript แบบ boilerplate ที่ยุ่งเหยิงหายไปได้ทันทีจำนวนมาก
  • ดีใจที่ได้เห็นบทความนี้ เริ่มใช้ TypeScript ในปี 2018 และหลังจากนั้นราว 2 ปี ก็เริ่มเชื่อว่านี่ไม่ใช่คำตอบที่สมบูรณ์โดยเนื้อแท้
    ผมมาจาก Haskell/C#/F# และแม้ TS จะมีระบบชนิดที่ทรงพลัง แต่ยกเว้นบางช่วงตอนพัฒนาแล้ว มันก็ไม่ได้มอบข้อดีแบบที่ภาษาเหล่านั้นให้มากนัก เมื่อต้องจัดการกับโลกความเป็นจริง มันมีข้อจำกัดในทางปฏิบัติมากกว่า C# อย่างชัดเจน ถ้าไม่ตระหนักว่าหลังคอมไพล์แล้วมันกลายเป็น JS ที่ไม่มีการตรวจสอบต่อ คุณก็ต้องคอยระวัง abstraction ที่รั่ว อยู่เสมอเพื่อหลีกเลี่ยงบั๊กที่ไม่ควรเกิดขึ้นได้ในเครื่องมือที่อ้างว่ามี static type

    • เป้าหมายของ TypeScript คือการรันบนเว็บมาโดยตลอด ไม่ได้ตั้งใจจะให้เหนือกว่า C# ในเชิงภาษา
      ถ้าต้องพัฒนา .NET ก็ใช้ C# ถ้าต้องพัฒนาเว็บก็ใช้ TypeScript ประมาณนั้น C# เป็น nominal type และมี reified generics ที่ค่อนข้างดี ส่วน TypeScript เป็น structural type และยอมแลกความ soundness เพื่อให้มีระบบชนิดที่ทรงพลังสำหรับแสดงความสัมพันธ์ของชนิดที่ซับซ้อน
      ไม่แน่ใจว่าภาษาที่รวมสองอย่างนี้เข้าด้วยกันจะดีหรือไม่ และแม้ทั้งสองภาษาจะทำงานได้ดีในทิศทางของตัวเอง แต่ทิศทางเหล่านั้นก็ไม่ได้เข้ากันนัก
  • สำหรับการตรวจสอบข้อมูลตอนรันไทม์ ผมพอใจกับการใช้ https://zod.dev อย่างมาก
    มันค่อนข้างเจ๋งที่สามารถแสดงเจตนาได้ตรงนั้นเลยด้วย API ที่ลื่นไหล โดยไม่ต้องนิยาม nominal type แยกต่างหาก

    • Nominal type มีจุดประสงค์การใช้งานคนละแบบ
      ตัวอย่างง่าย ๆ คือจำเป็นสำหรับการแยกเลข 2 ออกจากจำนวนเงินสกุลเงิน 2 ในโดเมน