3 คะแนน โดย GN⁺ 2024-07-26 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • มีการ merge PR #53725 ของ Node.js ทำให้รองรับการ รันไฟล์ TypeScript แบบทดลองผ่านแฟลก --experimental-strip-types
  • ฟีเจอร์นี้จะ ลบไวยากรณ์ของ type ออกจากซอร์ส TypeScript แล้วแปลงเป็นซอร์ส JavaScript โดย ไม่ทำการตรวจสอบ type ระหว่างการแปลง
  • การติดตั้งใช้งานเริ่มต้นจะไม่ทำ การแปลงฟีเจอร์เฉพาะของ TypeScript อย่าง Enum, namespaces เป็นต้น จึงจำกัดอยู่กับโค้ดที่เมื่อเอา type ออกแล้วกลายเป็น JavaScript ได้ทันที
  • เลือกใช้ @swc/wasm-typescript สำหรับการติดตั้งใช้งาน เพราะมี ความเรียบง่าย โดยไม่ต้องเพิ่ม Rust หรือ Go เข้าไปใน toolchain ของ Node.js
  • ในการอภิปรายมีการพูดถึงข้อจำกัดหลักอย่างกฎเรื่องนามสกุลไฟล์บังคับของ ESM, แนวปฏิบัติของ TypeScript ที่ import .js ทั้งที่ไฟล์จริงเป็น .ts, รวมถึงต้นทุนของ source map และตำแหน่งใน error stack

วิธีการทำงานของ --experimental-strip-types

  • เมื่อเปิดใช้ --experimental-strip-types แล้ว Node.js จะสามารถ รันไฟล์ TypeScript ได้
  • Node.js จะ transpile ซอร์สโค้ด TypeScript ให้เป็นซอร์สโค้ด JavaScript
  • ระหว่างการ transpile จะ ไม่ทำ type checking และข้อมูล type จะถูกทิ้งไป
  • ตัวอย่างมีดังนี้
const foo: string = "foo";
const foo = "foo";

เหตุผลที่เลือกแค่การลบ type และข้อจำกัดช่วงแรก

  • แกนหลักของ PR นี้คือ การลบไวยากรณ์ type ออกจากไวยากรณ์ของ TypeScript
  • runtime อื่นบางตัวอาจแปลงฟีเจอร์เฉพาะของ TypeScript ที่ไม่มีใน JavaScript เช่น enum ให้เป็น JavaScript
  • การติดตั้งใช้งานเริ่มต้นของ PR นี้จะไม่ทำการแปลงลักษณะนั้น
    • ใช้ Enum ไม่ได้
    • ใช้ namespaces ไม่ได้
    • ฟีเจอร์เฉพาะของ TypeScript อื่น ๆ ก็ไม่อยู่ในขอบเขตการแปลง
  • มีคำแนะนำให้ดูรายละเอียดการติดตั้งใช้งานและข้อจำกัดใน typescript.md ของรายการเปลี่ยนแปลงใน PR

เลือก @swc/wasm-typescript เป็นเครื่องมือสำหรับการติดตั้งใช้งาน

  • @swc/wasm-typescript เป็น แพ็กเกจขนาดเล็ก ที่ประกอบด้วยไฟล์ wasm และไฟล์ JavaScript สำหรับ binding เข้าด้วยกัน
  • เครื่องมืออื่นต้องการเพิ่ม Rust หรือ Go เข้าไปใน toolchain ของ Node.js
  • SWC ถูกใช้ใน Deno เพื่อจุดประสงค์เดียวกันอยู่แล้ว และผู้เขียน PR มองว่านี่เป็น ตัวเลือกที่ผ่านการพิสูจน์แล้ว
  • ในอนาคต ฟีเจอร์นี้อาจถูกติดตั้งใน ชั้น native ได้
  • PR นี้อ้างอิง nodejs/loaders#217

การตีความ import และประเด็นเรื่องนามสกุลไฟล์

  • import ที่ไม่ระบุนามสกุลไฟล์ อย่าง import foo from "./foo" จะไม่ถูกรองรับ
  • ใน ESM ของ Node.js มีกฎ mandatory file extensions อยู่แล้ว ดังนั้นการไม่รองรับ import แบบไม่มีนามสกุลจึงถูกมองว่าเป็นพฤติกรรมที่ตั้งใจไว้
  • มีความเห็นว่าใน codebase แบบโมดูลของ TypeScript มักพบกรณีที่ในคำสั่ง import ใช้นามสกุล .js ทั้งที่ไฟล์จริงเป็น .ts
    • ตัวอย่าง: import foo from './foo.js'
    • ในระบบไฟล์จริงอาจไม่มี ./foo.js และมีแค่ ./foo.ts
  • ผู้เขียน PR มองว่าปัญหานี้สามารถจัดการได้ในการทำงานรอบถัดไป และการเดานามสกุลไฟล์ต้องอาศัยการตรวจสอบระบบไฟล์เพิ่มและ heuristic
    • ถ้าไม่มี ./foo.js ก็ตรวจ ./foo.ts
    • ถ้าไม่มี ./foo.ts ก็ตรวจ ./foo.js
  • การอภิปรายนี้ติดตามอยู่ใน nodejs/loaders#208

ตำแหน่งซอร์ส, source map และ error stack

  • ในการรีวิวมีความเห็นว่าระยะสั้นจำเป็นต้องมี ตำแหน่งซอร์สที่แม่นยำ ใน error stack
  • วิธีหนึ่งที่เป็นไปได้คือทำให้กระบวนการลบ type ไม่เปลี่ยนตำแหน่งในซอร์ส เพื่อ หลีกเลี่ยงการสร้างและการแมป source map
  • มีการกล่าวถึงข้อจำกัดว่า source map มีต้นทุนทั้งด้านการใช้หน่วยความจำและประสิทธิภาพ และไม่ใช่เรื่องง่าย
  • ผู้เขียน PR มองว่า SWC รองรับ source map อยู่แล้ว จึงอาจเพิ่มแฟลกสำหรับเปิดใช้งานได้
  • อีกทางหนึ่งอาจขอให้ SWC รองรับตัวเลือกที่แทนไวยากรณ์ที่ถูกลบด้วยช่องว่าง เพื่อไม่ให้ error stack เดิมเปลี่ยนไป

ประเด็นด้านการออกแบบที่ยังเหลือก่อนทำให้เสถียร

  • การรองรับ TypeScript แบ่งได้เป็น 2 แกน
    • การลบไวยากรณ์ type
    • การปรับกฎการตีความเพื่อให้รับไฟล์ .ts
  • มีความกังวลว่าการเปลี่ยนกฎการตีความอาจชนกับ TypeScript loader ที่มีอยู่เดิม
  • ผู้เขียน PR ตอบว่า PR นี้ไม่ได้ override กฎการตีความ และยังต้องระบุนามสกุลไฟล์อยู่
  • มีความเห็นว่าสามารถรองรับรูปแบบ import './file.ts' ได้ผ่าน allowImportingTsExtensions และแนวทางนี้ก็สอดคล้องกับ Deno
  • หากต้องการรันไฟล์ซอร์สเดียวกันโดยตรงและให้ build tool สร้างผลลัพธ์ JavaScript ด้วย ก็อาจให้ build tool รับผิดชอบการเปลี่ยน file.ts เป็น file.js

การ merge และงานต่อเนื่อง

  • PR ถูก merge เข้า nodejs:main เมื่อวันที่ 24 กรกฎาคม 2024
  • ขนาดการเปลี่ยนแปลงคือ 8 commits, 89 files, เพิ่ม 2,190 บรรทัด และลบ 25 บรรทัด
  • มีป้ายกำกับอย่าง semver-minor, experimental, module, esm, notable-change
  • มีการพูดคุยว่าควรกล่าวถึงฟีเจอร์นี้ในหน้าคู่มือ TypeScript ของเว็บไซต์หรือไม่ และมี PR สำหรับอัปเดตที่เกี่ยวข้องเชื่อมไว้ด้วย
  • มีการอภิปรายเป็น issue ถัดไปเกี่ยวกับการใส่ เวอร์ชัน TypeScript ที่ Node binary ใช้สำหรับการลบ type ลงใน metadata ของการเผยแพร่

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

 
GN⁺ 2024-07-26
ความคิดเห็นใน Hacker News
  • การลบ type ของ TypeScript ทำไม่ได้หากไม่มีไวยากรณ์ของ TypeScript การลบ type ไม่ใช่งานระดับ token และไวยากรณ์ของ TypeScript ก็เปลี่ยนแปลงอยู่เรื่อย ๆ
    ตัวอย่างเช่น foo < bar & baz > ( x ) ใน TypeScript 1.5 นั้น bar&baz ไม่ใช่นิพจน์ type ที่ถูกต้อง จึงถูก parse เป็นเหมือน (foo (x)) แต่หลังจากมีการเพิ่มตัวดำเนินการ intersection type แล้ว มันจะถูก parse เป็น foo<(bar & baz)>(x) และถูกทำให้ไม่กำกวมเป็น foo(x)
    หากต้องการใช้ฟีเจอร์ TypeScript ล่าสุดต่อไป ก็ต้อง compile เป็น JS หรือคอยอัปเดตเวอร์ชัน Node ให้ใหม่อยู่เสมอ ซึ่งอาจเป็นการประนีประนอมที่ทีมที่ชอบอยู่กับ Node LTS ยอมรับได้ยาก

    • ดูเหมือนทีม Node ก็พิจารณาปัญหานี้ไว้แล้วเช่นกัน มี precedent ที่ Node bundle npm มาให้แต่สามารถอัปเกรดแยกต่างหากได้ จึงมีการหารือว่าตัวแปลง TypeScript ก็น่าจะทำแบบคล้ายกันได้
      แนวทางคือใส่เวอร์ชันเสถียรไว้ใน core แต่ถ้า TypeScript ออกฟีเจอร์ใหม่หรือ breaking changes หรือผู้ใช้ต้องการใช้ฟีเจอร์ทดลองใหม่ ก็ให้อัปเกรดแยกต่างหากจาก NPM
      วิธีนี้จะทำให้ผู้ใช้ไม่ถูกล็อกไว้ ขณะเดียวกันก็ยังรองรับ TypeScript เวอร์ชันหนึ่ง ๆ ได้ตลอดอายุ 3 ปีของ Node.js release: https://github.com/nodejs/loaders/issues/217
    • หากมองจากมุมของการลบ type แล้ว มี TypeScript หลายเวอร์ชันมากที่ไวยากรณ์เสถียรกว่าช่วงที่ไม่เสถียร ที่ย้อนกลับไปถึง 1.5 ก็เพราะตั้งแต่ 2.x เป็นต้นมามันค่อนข้างเสถียร
      การเปลี่ยนแปลงไวยากรณ์ครั้งใหญ่ล่าสุดน่าจะเป็น conditional types ใน 2.8 ซึ่งเป็นช่วงที่ไวยากรณ์คล้ายตัวดำเนินการสามทาง if ถูกใส่เข้ามาในตำแหน่งของ type ถ้าต้องทำถึงขั้นตรวจ type ด้วย type model เปลี่ยนไปมากตั้งแต่ 2.x แต่ไวยากรณ์โดยรวมค่อนข้างเสถียร และนวัตกรรมส่วนใหญ่ของ TypeScript ก็อยู่ด้าน type model กับ type inference
      แม้ยังอยู่ใน Stage 1 ซึ่งเป็นระยะต้น ๆ แต่ก็มีความพยายามจะทำให้ไวยากรณ์ type ของ TypeScript ส่วนใหญ่เป็นมาตรฐานสำหรับจุดประสงค์การลบ type ไม่ใช่การตรวจ type: https://github.com/tc39/proposal-type-annotations
    • compiler ก็ต้องอัปเดตไปด้วย สุดท้ายอาจพูดได้ว่า “ไม่สามารถอัปเวอร์ชัน TypeScript แยกจากเวอร์ชัน Node.js ได้” แต่คงไม่ใช่ปัญหาใหญ่
    • การใช้เครื่องหมายอสมการเหมือนเป็น วงเล็บมุม น่าจะเป็นความผิดพลาดจริง ๆ
    • ทีมที่ตาม TypeScript ล่าสุดอยู่เสมอและใช้ฟีเจอร์ภาษาใหม่ ๆ ก็น่าจะตามเวอร์ชัน Node ล่าสุดด้วยเช่นกัน จากประสบการณ์ การอัปเกรด TypeScript มักต้อง migration หรือแก้ไขเพราะ error ใหม่ ๆ บ่อยกว่าการอัปเกรด Node
      ทีมที่ไม่ค่อยสนใจ V8 หรือฟีเจอร์ Node ล่าสุดและอยู่กับ LTS ก็น่าจะมีโอกาสน้อยที่จะสนใจฟีเจอร์ TypeScript ล่าสุดเช่นกัน
  • หาก Node.js สามารถรันไฟล์ TypeScript ได้โดยตรง compiler ของ TypeScript ก็อาจถูกใช้เป็นเพียง type checker แทนการลบ type และแปลงเป็น JavaScript คล้ายกับใน Python ที่ type checker ตรวจ type แล้วปล่อยไว้ตามเดิม ส่วน interpreter จะเพิกเฉยต่อ type hint
    ที่น่าสนใจคือ ใน Python มี type checker ยอดนิยมหลายตัวที่ใช้ไวยากรณ์ type hint เดียวกันแต่มี semantics ต่างกัน ขณะที่ใน JavaScript ดูเหมือน TypeScript จะกลายเป็น type checker ยอดนิยมเพียงตัวเดียวโดยพฤตินัย
    เคยได้ยินด้วยว่าใน Python บางคนเขียน type ไว้ใน source โดยไม่ตรวจ type ใช้เหมือนไวยากรณ์คอมเมนต์ที่สะดวกกว่า หาก Node.js รองรับการเพิกเฉยต่อ type วิธีแบบนั้นก็จะเป็นไปได้ใน JavaScript เช่นกัน

    • Flow ของ Facebook เคยค่อนข้างสำคัญในฝั่ง JavaScript เมื่อหลายปีก่อน แต่ตอนนี้ดูเหมือน TypeScript จะชนะค่อนข้างชัดเจนแล้ว
    • แม้จะไม่ทำ type checking แต่ IDE ก็สามารถใช้ type hint เพื่อปรับปรุง autocomplete และอื่น ๆ ได้ ดังนั้นการเพิ่ม type ในบางตำแหน่งก็ยังมีความหมายอยู่
    • ทุกวันนี้ก็สามารถใส่ type ด้วย JSDoc แล้วให้ TypeScript ตรวจสอบได้โดยไม่ต้อง compile ทำให้ build เร็วขึ้น และได้โค้ดที่ทำงานได้ทุกที่โดยไม่ต้องมีการประมวลผลแบบเวทมนตร์หรือขั้นตอนลบสิ่งอื่นนอกจากคอมเมนต์
      โดยส่วนตัว ความไม่สะดวกที่สุดของการใช้ JSDoc คือไวยากรณ์ import แต่หลังจาก TypeScript 5.5 เปลี่ยนไป ตอนนี้ก็ไม่ใช่ปัญหาแล้ว
    • Node.js ไม่ใช่ JavaScript runtime เพียงอย่างเดียว จนกว่าทุก browser จะรัน TypeScript ได้โดยตรง ก็ยังต้อง compile TS เป็น JS สำหรับ browser
      อย่างไรก็ตาม bundler บางตัวก็ใช้ compiler ไม่เป็นทางการอย่าง SWC ทำงานนี้อยู่แล้ว และ Node เองก็กำลังทดสอบ SWC สำหรับฟีเจอร์นี้ด้วย
      type hint ไม่ใช่แค่คอมเมนต์ธรรมดา แต่ยังเป็น hint ให้ IDE แสดง autocomplete ที่ดีขึ้น เช่น ชื่อด้วย
    • มี ข้อเสนอ EcmaScript ในทิศทางนี้: https://github.com/tc39/proposal-type-annotations
      คิดว่าสิ่งนี้ควรเป็นส่วนหนึ่งของ language specification
  • สงสัยว่าถ้าฟีเจอร์นี้ไม่ได้อยู่หลังแฟล็ก แต่กลายเป็นค่าเริ่มต้นแล้ว ระบบนิเวศ NPM จะตอบสนองอย่างไร ไม่รู้ว่าผู้มีส่วนร่วมจะยังคง build เวอร์ชัน CJS และ ESM ตอนเผยแพร่โมดูล NPM อยู่ไหม หรือจะแค่ใส่ engine: nodejs >= 25 ใน package.json แล้วข้ามขั้นตอน build ไปเลย
    ส่วนตัวคิดว่าถ้าโมดูล NPM ที่มีซอร์ส TS เลิกแจกจ่าย dist/.cjs ก็น่าจะดีกว่า เพราะจะชัดเจนขึ้นว่าควรวาง debugger กับ console.log ไว้ตรงไหน จากมุมของผู้มีส่วนร่วมใน NPM สิ่งยั่วยวนให้ตัดขั้นตอน build ออกก็คงแรงมากเช่นกัน
    แต่แค่ dependency ตัวเดียวแจกจ่ายไฟล์ TS สมมติฐานที่ว่า “ทุกคนรับไฟล์ TS” ก็อาจแพร่กระจายอย่างรวดเร็วได้ ทันทีที่ Node.js เอาสิ่งนี้ออกจากแฟล็กทดลองแบบ opt-in ทั้งคอมมูนิตี้ก็น่าจะเริ่มคาดหวังโดยนัยให้ผู้บริโภครองรับไฟล์ TS
    ถ้าเป็นแบบนั้น ภายในไม่กี่เดือน Firefox กับ Safari ก็คงถูกกดดันให้ยอมรับด้วย และคอมไพเลอร์ JS ทั้งหมดก็จะไม่มีทางเลือกนอกจากทิ้ง annotation ของชนิดข้อมูลใน TS
    ส่วนตัวแล้วแบบนี้ก็โอเคเหมือนกัน เพราะฝั่งที่ใช้ TS เองก็ใส่ขั้นตอนแปลงโค้ด TS เป็น JS และ .d.ts ลงในโมดูล NPM เพื่อรองรับผู้ใช้ JS ในจินตนาการบางกลุ่มอยู่แล้ว แต่ถ้า Node รับไฟล์ .ts ได้ ก็อาจลบขั้นตอนแปลงนั้นออกโดยไม่ทันสังเกต เลยสงสัยว่ามีกลไกอะไรที่จะป้องกันไม่ให้ผู้เผยแพร่ NPM เลิกแจกไฟล์ js/d.ts แล้วไม่รู้ว่ามันพัง

    • Ryan Dahl กำลังสร้าง package registry ใหม่ชื่อ JSR เพื่อแก้ปัญหานี้
      เป็นแนวทางที่ให้อัปโหลดโค้ด TypeScript ได้โดยไม่ต้องมีขั้นตอน build และเมื่อ developer คนอื่นติดตั้ง ก็จะเห็นซอร์ส TypeScript ต้นฉบับ ไม่ใช่ JavaScript ที่ถูกแปลงแล้ว
    • ไม่รองรับ การรันไฟล์ .ts ภายใน node_modules นี่เป็นหนึ่งในข้อจำกัดหลักเพื่อไม่ให้ระบบนิเวศพัง
    • ไลบรารี TypeScript เก่า ๆ ที่ยังดูแลอยู่กำลังถูกแปลงเป็น .cjs และ .mjs สำหรับ npm อยู่ และคงจะต้องแจกจ่ายทั้งสามเวอร์ชัน
      ถ้าเป็นโปรเจกต์ใหม่ที่เขียนจากศูนย์ ก็อาจแจกเฉพาะ TypeScript และไม่แปลงก็ได้
      แก้ไข: ตามคอมเมนต์พี่น้องของ satanacchio ซึ่งดูเหมือนจะเป็นผู้เขียน PR และสมาชิกคณะกรรมการกำกับด้านเทคนิคของ Node.js การรองรับ TS มีไว้สำหรับรายการระดับบนสุด และไม่ใช้กับไลบรารีใน node_modules
    • ผมอยากเผยแพร่ .ts ซึ่งเป็นซอร์สโค้ดของตัวเองลง npm แต่ทีม TypeScript คัดค้านเรื่องนี้อย่างมากเพราะปัญหา tsconfig และปัญหาประสิทธิภาพ ถึงอย่างนั้นก็ยังคาดหวังอยู่
  • สักวันหนึ่ง Node อาจทำให้สามารถ สะท้อนข้อมูลชนิดขณะรันไทม์ จาก JS ได้
    ถ้าเป็นแบบนั้นจะได้ประโยชน์มหาศาล ใน Python เพราะสามารถสะท้อนข้อมูลชนิดได้ เครื่องมือดี ๆ อย่าง pydantic จึงสร้างการตรวจสอบจากชนิดข้อมูลได้
    ด้วยสัญกรณ์มาตรฐานเดียว ก็จะได้ทั้งการตรวจชนิด, การตรวจข้อมูลขณะรันไทม์, การสร้าง API และการสร้างเอกสาร API
    ตอนนี้ใน JS เครื่องมืออย่าง Zod ต้องแทบจะประดิษฐ์สิ่งที่ TypeScript ทำอยู่แล้วขึ้นมาใหม่ เช่น const mySchema = z.string();

    • z.string() ของ Zod ไม่ได้ให้แค่ type safety แบบ TypeScript เท่านั้น TypeScript ให้การตรวจชนิดตอนคอมไพล์ ส่วน Zod เพิ่ม การตรวจสอบและการ parse ขณะรันไทม์
      z.string() ทำให้ mySchema กลายเป็น schema เชิงฟังก์ชันที่ parse และตรวจสอบได้ mySchema.parse("some data") จะสำเร็จ ส่วน mySchema.parse(321) จะโยน exception
      เคยลองใช้ในจุดที่ต้องการการตรวจสอบขณะรันไทม์และการยืนยันภายในโปรเซส มันทำงานได้ค่อนข้างดี และยังดึง type ออกมาได้ด้วย เช่น const A = z.string(); type A = z.infer; // string
      กล่าวคือ ถ้ากำหนด type ใน Zod ก่อนแล้วอนุมาน type จากตรงนั้น ก็จะได้ทั้งการตรวจชนิดตอนคอมไพล์และขณะรันไทม์ เพียงแต่สำหรับ codebase เล็ก ๆ ที่เคลื่อนไหวเร็วอาจหนักเกินไป แต่ในสถานการณ์ที่ต้องตรวจสอบภายในโปรเซส มันค่อนข้างมีประโยชน์และภาระงานก็ไม่มาก
    • ช่วงนี้โฟกัสของทีม TypeScript อยู่ที่การจัดแนวกับภาษา JavaScript เป็นส่วนใหญ่ และฟีเจอร์ใหม่อย่าง runtime type แทบถูกกันออกไป หรืออย่างน้อยก็ถูกเลื่อนไปไว้หลังข้อเสนอเรื่อง type ของ JavaScript ใน TC39 ปัญหาเรื่องการใช้ decorator กับตัวแปรนอกโครงสร้าง class ก็คล้ายกัน
      อย่างไรก็ตาม TypeScript อนุญาตให้ใช้ปลั๊กอินได้ แม้จะไม่ได้ถูกใช้กันมากนัก แต่สามารถเพิ่มฟีเจอร์อื่นเข้าไปในภาษาแล้วแปลงเป็น JavaScript ผลลัพธ์ได้
      ปลั๊กอินที่เกี่ยวกับ runtime type มี Typia ซึ่งทำให้สามารถใช้ signature ของ TypeScript type ตอนรันไทม์ใน guard อย่าง assert(myValue) ได้ มันจะดักจับการเรียกฟังก์ชันนี้ แล้วสร้างคำสั่ง if แบบ exhaustive ใน JavaScript ที่ถูกแปลง เพื่อเช็กลักษณะของตัวแปรที่ส่งเข้าไป
      คงยังไม่น่าจะกลายเป็นส่วนหนึ่งของภาษาในอีก 4–6 ปีข้างหน้า แต่ก็มีไลบรารีที่ทำให้ทำเรื่องแบบนี้ได้อยู่แล้วในวันนี้
    • ถ้า JS จะเพิ่มการตรวจชนิดเข้าสักวัน ก็หวังว่าจะไม่เลือก TypeScript
      จริง ๆ แล้วต้องการ ระบบชนิดที่ sound แต่ TS ตั้งใจออกแบบมาให้ไม่ sound ต้องการระบบชนิดที่ไม่ยอมให้ทำแนวปฏิบัติการเขียนโค้ดแย่ ๆ แบบ TS และบังคับการออกแบบที่ทำให้โปรแกรมเร็วขึ้น ต้องการระบบชนิดแบบ Hindley-Milner
      ถ้าอยากทำโมดูลให้เป็นโค้ดที่มี type ก็แค่เพิ่ม "use type" อะไรทำนองนั้น โหมดนี้ควรห้ามส่วนแย่ ๆ อย่างการแปลงชนิดแบบบังคับ และป้องกันปัจจัยที่ทำให้ประสิทธิภาพเสีย เช่น การเปลี่ยนรูปทรง object หรือ type ของค่า หรือการสร้าง array ที่ผสมค่าอะไรก็ได้
      ข้อมูลที่เข้ามาจากโมดูลที่ไม่มี type จะถูกแปลง หรือถ้าแปลงไม่ได้ก็โยน error แล้วคอมไพเลอร์จะสามารถ optimize โค้ดที่มี type ได้ลึกขึ้นภายใต้การรับประกัน type ที่แข็งแรงกว่าและไม่มีความเสี่ยงต้องล้มเลิกกลางทาง
    • สงสัยว่านี่หมายความว่า Node จะรู้ได้ไหมว่า exception เป็น subclass ของ ValueError หรือ object เป็น instance ของ SomeClass หรือเปล่า ผมยังเป็นมือใหม่ TS เลยคิดว่า JS ไม่มี type อื่นนอกจาก array, object, number, string และ Zod หรือฟังก์ชัน type guard ก็คืน object ทั่วไปในระดับ “เชื่อฉันเถอะ”
  • ประสบการณ์นักพัฒนาของ Bun อยู่ในระดับที่ค่อนข้างไม่เคยมีมาก่อนในด้านนี้ และตอนนี้ use case ส่วนใหญ่ก็ครอบคลุมแล้ว หรือเมื่อเขียนสคริปต์รันด้วย bun run Bun ก็ไม่ crash
    ในทางกลับกัน Node ไม่สามารถตั้งค่าให้ไม่ต้องระบุนามสกุลไฟล์ใน import ได้ และก็ไม่สามารถทำให้ tsc เติมนามสกุล .js ให้ผลลัพธ์ที่คอมไพล์แล้วโดยอัตโนมัติได้ จึงต้องเพิ่ม bundler เข้ามา
    การรองรับ TypeScript แบบ native น่าจะช่วยแก้ความไม่สะดวกเล็ก ๆ นี้ได้พอสมควร แต่แม้จะเสถียรแล้ว ก็ยากจะจินตนาการว่าประสบการณ์ผู้ใช้หรือประสิทธิภาพจะทัดเทียม Bun ได้

    • ควรต้องระบุนามสกุลไฟล์ การค้นหา path บนเครือข่ายทำแบบเดียวกับดิสก์ในเครื่องไม่ได้ และ runtime ที่เชื่อมต่อเครือข่าย อย่างเบราว์เซอร์ก็สำคัญมากสำหรับ JavaScript
    • ชอบ Bun มาก แต่ Deno ยังคง mature และเสถียรกว่า อีกทั้งมีฟีเจอร์มากกว่า มีอย่าง worker ที่เสถียร, HTTP/2 และบาง use case V8 อาจเร็วกว่า JSC ด้วย
      ประสบการณ์นักพัฒนาและเครื่องมือก็อยู่ระดับยอดเยี่ยม Deno ตรวจ type ได้ด้วย และเท่าที่จำได้ก็ bundle TSC มาให้
      Bun เป็นกระแสแรง แต่สำหรับงานจริงจัง ตอนนี้ Deno เป็นตัวเลือกที่ดีกว่าอย่างชัดเจน ถึงอย่างนั้น vision และการลงมือทำของ Bun ก็น่าประทับใจ และเป็นเรื่องดีสำหรับนักพัฒนา
    • ลอง Bun สองครั้งห่างกันหลายเดือน แต่บน Windows รัน bun install แล้วล้มเหลว เลยไม่เคยทำงานได้จริงสักครั้ง: https://github.com/search?q=repo%3Aoven-sh%2Fbun+bun+install...
    • ถ้าบอกว่า “ไม่เคยมีมาก่อน” ก็คงใช่ถ้าไม่นับ Deno
    • Bun ยังดูดิบเกินไปหรือเปล่า ทำด้วย Zig ที่ยังไม่ออก 1.0 ด้วยซ้ำ
  • อยากได้ runtime สำหรับ TypeScript มานาน และก็ชอบ TypeScript มากจริง ๆ แต่ก็ตลกดีที่ออกจาก Java มานานแล้ว สุดท้ายกลับมาหาอะไรบางอย่างที่ใกล้ Java กว่ามาก
    สุดท้ายสิ่งที่เราอยากได้คงเป็น Java ที่มี JIT, type system ที่สมบูรณ์กว่า และ gradual typing แม้ ecosystem ของ npm จะมีข้อเสียมากมาย แต่การใช้ library ใน ecosystem นี้รู้สึกกดดันน้อยกว่ามากและสนุกกว่า
    น่าแปลกที่ Rust อยู่คนละปลายของสเปกตรัมภาษา แต่บางทีก็ให้ความรู้สึกคล้ายกัน
    แก้ไข: คำว่า JIT ไม่เหมาะนัก ผมเขียนว่า JIT แบบขี้เกียจ แต่สิ่งที่ตั้งใจจะพูดคือความต่างของเวลาเริ่มต้นและ runtime เวลาเรียกใช้อะไรบางอย่างบน JVM กับ V8 Java ให้ความรู้สึกหนัก แต่ ecosystem ของ JavaScript ให้ความรู้สึกคล่องตัวกว่ามาก

    • Java เป็นสิ่งที่ทำให้คำว่า JIT โด่งดังขึ้นมาแบบตรงตัว เลยไม่เข้าใจว่า “Java ที่มี JIT” หมายถึงอะไร
      และก็ไม่เข้าใจเลยว่า TypeScript ใกล้ Java มากกว่าตรงไหน สิ่งที่ทั้งสองมีร่วมกันมีแค่ชื่อที่ตั้งผิดว่า “Javascript” กับการรองรับ imperative programming เท่านั้น ที่เหลือมองว่าต่างกันมาก
    • จากประสบการณ์ TypeScript ดีกว่า Java มาก พูดเยิ่นเย้อน้อยกว่ามากและยืดหยุ่นกว่า
    • ส่วนตัวชอบใช้ TypeScript มากกว่า Java มาก โดยเฉพาะถ้าเลี่ยงส่วนของ class ที่ยังไม่สมบูรณ์นัก แล้วโฟกัสที่ interface กับ function จะใช้งานได้ดีกว่ามาก
      แต่สิ่งที่ Java มีและคิดถึงใน TypeScript คือ runtime type reflection ecosystem ของ TypeScript มีวิธี workaround เรื่องนี้นับไม่ถ้วน แต่รู้สึกว่าทุกวิธีดูไม่ค่อยสวย
    • Java มี JIT และก็สงสัยว่า type system ของ TypeScript มีฟีเจอร์มากกว่า Java อย่างไร
    • ผมก็อยู่ในจุดคล้าย ๆ กัน ส่วนตัวสิ่งที่อยากได้คือ standard library และ ecosystem ของ C# โดยตัดส่วน legacy หรือส่วนที่แทบถูกทิ้งไปแล้วในปัจจุบันออก แล้วเพิ่มความง่ายและพลังของ structural algebraic types เข้าไป
      AoT ก็ใช้ได้ และถ้ามีตัวเลือกเป็น binary เดี่ยวได้ก็จะดี อุดมคติคือไม่มี runtime และสามารถตัดส่วนที่ไม่ใช้ได้อย่างชาญฉลาด ถ้ารันด้วย JIT ในเบราว์เซอร์ได้ด้วยก็ยิ่งดี
      ยังอยากได้ความสะดวกจาก compiler และ type checker เช่น exhaustive pattern matching ด้วย
  • ฟีเจอร์ของ Deno ที่ผมชอบกำลังเข้ามาใน Node โดยตรง ดีมาก
    ต่อไปอาจไม่ต้องติดตั้ง esbuild ตลอดเวลาเพื่อ strip type แล้วก็ได้ คาดหวังว่าจะเขียนสคริปต์ด้วย TypeScript ได้ง่ายขึ้นมาก
    ช่วงหลังผมชอบใช้ Python สำหรับสคริปต์ใช้ครั้งเดียวมากกว่า แต่ในแง่ type ส่วนตัวคิดว่า TypeScript ดีกว่า Python โดยเฉพาะสคริปต์ใหญ่ ๆ ที่กลับมาดูอีกทีหลังผ่านไปหลายเดือน จะได้ประโยชน์จาก type มาก

    • คนที่อยากรัน TS บน Node มี tsx อยู่แล้ว มี ts-node ด้วย แต่ส่วนตัวชอบ tsx มากกว่า
      https://github.com/privatenumber/tsx
    • สงสัยว่าเคยลองใช้ https://github.com/google/zx ไหม
    • พูดให้ถูกคือ นี่เป็นฟีเจอร์เดียวของ Deno ที่ผมอยากได้
  • เป็นเดือนที่มีเรื่องเกิดขึ้นกับ Node เยอะจริง ๆ ก่อนอื่น v22.5.0 เพิ่ม node:sqlite เข้ามา และตอนนี้การรองรับ TypeScript ก็กำลังเข้ามาอีก ชอบทิศทางที่ Node กำลังไป

    • น่าจะเป็นผลจากอิทธิพลหรือการแข่งขันกับ Bun เป็นเรื่องดีสำหรับทุกคน
    • test runner ที่เพิ่มเข้ามาเมื่อไม่นานนี้ก็ยอดเยี่ยมมาก
  • ผมเป็นผู้เขียน PR ถามอะไรก็ได้

    • อยากรู้ว่าขั้นตอนถัดไปคืออะไร และคุณมองว่า TypeScript จะมีอนาคตอย่างไรใน Node และ ecosystem ของ JS โดยรวม
  • ผมเริ่มใช้ Node.js ทำงานแบ็กเอนด์มานานแล้ว ซึ่งดูเหมือนจะมีข้อดีมากกว่าการเขียนโค้ดด้วย PHP หลายอย่าง และไม่ได้พ่วงปัญหาของ Java มามากนัก
    แต่ Node ค่อนข้างหยาบ ๆ อยู่บ้าง และรู้สึกเหมือนเป็นภาษาที่ถ้าจะทำให้ได้ภาษาตามที่ต้องการก็ต้องเอาโน่นนี่มาต่อเติมเข้าไป สุดท้ายผมก็เริ่มใช้ Go ซึ่งใช้ง่ายกว่ามาก และแม้บางครั้งจะเยิ่นเย้อกว่ามาก แต่ความปลอดภัยของชนิดข้อมูลก็ทำให้การเขียนโค้ดง่ายขึ้น
    TypeScript ดูเหมือนเป็นตัวเลือกที่ดี แต่สุดท้ายก็เป็นการต่อเติมอีกชั้นหนึ่งอยู่ดี ผมไม่ค่อยแน่ใจว่าเมื่อใช้ TypeScript แทน Go แล้วจะได้คุณค่าอะไรเพิ่มขึ้น การมีชนิดข้อมูลที่ชัดเจนนั้นดี แต่ก็ไม่ได้แก้ปัญหาอื่น ๆ ของภาษาที่ Go แก้ไปแล้ว Deno ก็แก้ได้บางส่วน
    ข้อได้เปรียบใหญ่ของ Node เหนือ Go คือสามารถทำต้นแบบได้อย่างรวดเร็ว แต่พอใช้ TypeScript ดูเหมือนข้อได้เปรียบนั้นจะลดลงไปมาก ดังนั้นจึงยากจะตัดสินว่านี่เป็นความก้าวหน้าที่ดี หรือเป็นการทำให้ Node สูญเสียคุณสมบัติที่เคยทำให้มันเป็นตัวเลือกที่ดี

    • TypeScript คือ JS ที่ปลอดภัยกว่า ถึงใช้ TS ก็ยังคงเขียน JS อยู่ดี
      จากคำว่า “ต่อเติม” ปัญหาอาจไม่ใช่ตัวภาษาเอง แต่อาจเป็นการไม่มี เฟรมเวิร์กที่มีจุดยืนชัดเจน ซึ่งจัดการทุกอย่างให้เป็นค่าเริ่มต้นแบบ Django ก็ได้ ผมชอบใช้ Django แต่การออกนอกเส้นทางที่กำหนดไว้ก็ยากขึ้นเล็กน้อย
    • คำตอบที่ชัดเจนคือความคุ้นเคยกับภาษา ถ้าโค้ดฝั่งฟรอนต์เอนด์ของโปรเจกต์เป็น JavaScript หรือ TypeScript การใช้ Node ก็เป็นตัวเลือกที่ง่าย สามารถใช้ไลบรารีร่วมกัน ชนิดข้อมูลร่วมกัน ฯลฯ ได้
    • อาจเป็นเพราะถ้ารู้อยู่แล้วว่า JavaScript หรือมีประสบการณ์ภายในบริษัทอยู่แล้ว ก็อาจดีกว่าการเรียน Go เราเองก็เคยมีประสบการณ์ฟรอนต์เอนด์มาก่อน จึงใช้มันร่วมกับ cdktf และดูเป็นตัวเลือกที่สมเหตุสมผลกว่า Go