diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..847fba0 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,24 @@ +name: Build Doc + +on: + pull_request: + branches: + - main + - develop + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Not needed if lastUpdated is not enabled + - uses: oven-sh/setup-bun@v1 # Uncomment this if you're using Bun + + - name: Install dependencies + run: bun install + + - name: Build with VitePress + run: bun run docs:build diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 6c80f62..e0bd78e 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -15,6 +15,7 @@ export default defineConfig({ items: sidebarASPNetCore(), }, "/paths/devops": { base: "/paths/devops/", items: sidebarDevOps() }, + "/paths/typescript": { base: "/paths/typescript/", items: sidebarTypeScript() }, "/paths/java": { base: "/paths/java/", items: sidebarJava() }, "/paths/azure": { base: "/paths/azure/", items: sidebarAzure() }, "/paths/cloud-computing": { @@ -37,6 +38,14 @@ export default defineConfig({ base: "/paths/mobile-development/", items: sidebarMobileApplicationDevelopment(), }, + "/paths/practices": { + base: "/paths/practices/", + items: sidebarPractices() + }, + "/paths/software-architecture": { + base: "/paths/software-architecture/", + items: sidebarSoftwareArchitecture() + } }, socialLinks: [ { icon: "github", link: "https://github.com/ThaiProgrammer/tpa-path" }, @@ -408,3 +417,91 @@ function sidebarMobileApplicationDevelopment(): DefaultTheme.SidebarItem[] { }, ]; } + +function sidebarPractices(): DefaultTheme.SidebarItem[] { + return [ + { + text: "Software Development Practices", + items: [ + { + text: "Design Practices", + collapsed: true, + base: "/paths/practices/design/", + items: [ + { text: "Design Patterns", link: "design-patterns" } + ] + }, + ] + } + ] +} + +function sidebarSoftwareArchitecture(): DefaultTheme.SidebarItem[] { + return [ + { + text: "Software Architecture", + items: [ + { + text: "Introduction", + collapsed: true, + base: "/paths/software-architecture/introduction/", + items: [ + { text: "What is software architecture?", link: "software-architecture-intro" }, + { text: "Styles & Patterns", link: "styles-n-patterns" }, + ] + } + ] + } + ] +} + +function sidebarTypeScript(): DefaultTheme.SidebarItem[] { + return [ + { + text: "TypeScript", + items: [ + { + text: "Types ที่ใช้งานเป็นประจำ", + collapsed: true, + base: "/paths/typescript/everyday-types/", + items: [ + { text: "Type คือข้อมูลชนิดหนึ่ง", link: "type-is-data" }, + { text: "การจัดการกับ Object", link: "object-manipulation" }, + { text: "Type Assertions", link: "runtime-type" }, + { text: "จัดการ Try-Catch Error", link: "catch-error" }, + { text: "จัดการ Runtime Type", link: "runtime-type" }, + ], + }, + { + text: "คู่มือ TypeScript", + collapsed: true, + base: "/paths/typescript/handbook/", + items: [ + { text: "Generics", link: "generics" }, + { text: "Utility Types", link: "utility-types" }, + { text: "conditional types (แบบมีเงื่อนไข)", link: "conditional-types" }, + { text: "Function Overload", link: "function-overload" }, + { text: "Enum", link: "enum" }, + { text: "Keyof", link: "keyof" }, + { text: "Narrowing Type", link: "narrowing-type" }, + { text: "Mapped Types", link: "mapped-types" }, + { text: "การปรับแต่ง Type", link: "type-manipulation" }, + ] + }, + { + text: "Cookbook", + collapsed: true, + base: "/paths/typescript/cookbook/", + items: [ + { text: "สร้าง enum ใช้เอง แบบที่ Key กับ Value เหมือกัน", link: "create-own-enum-string" }, + { text: "เลือกบาง Property จาก Record", link: "filter-some-property-of-record" }, + { text: "สร้าง Builder Pattern แบบ Type-Safe ", link: "type-safe-builder-pattern" }, + { text: "สร้าง union type from array", link: "create-union-type-from-array" }, + { text: "เพิ่ม Prefix ในแต่ละ Property Key ใน Object", link: "append-prefix-object-property-key" }, + { text: "Data Validation ด้วย Zod ใช้คู่กับ TypeScript ได้", link: "zod-typescript-integration" }, + ] + } + ], + }, + ]; +} diff --git a/index.md b/index.md index 822a64d..203f29d 100644 --- a/index.md +++ b/index.md @@ -20,6 +20,12 @@ hero: } features: + - title: Practices + details: กระบวนการ วิธีการ และหลักการที่ใช้ในการพัฒนาและดูแลรักษาซอฟต์แวร์ที่ใช้กันอย่างแพร่หลายในปัจจุบัน + link: /paths/practices + - title: Software Architecture + details: เนื้อหาเกี่ยวกับ Software Architecture รูปแบบต่างๆที่ใช้ในการพัฒนาซอฟต์แวร์ + link: /paths/software-architecture - title: Web Development Guideline details: รวบรวมเนื้อหาที่คิดว่าเป็นประโยชน์ให้เห็นภาพกว้างในการเข้ามาทำงานในสาย Web Developers link: /paths/web-guideline @@ -32,6 +38,9 @@ features: - title: Source Code Control details: แนวทางการดูแลและควบคุม Source Code ที่เราทำงานกันเป็นทีมได้อย่างมีประสิทธิภาพครับ link: /paths/sourcecodecontrol + - title: TypeScript + details: เนื้อหาที่เกี่ยวกับ TypeScript การใช้งาน Type ในชีวิตจริง และคู่มือการใช้งานและ Cookbook + link: /paths/typescript - title: ASP.Net Core details: ขั้นตอนแนะนำในการเตรียมความพร้อมเพื่อพัฒนา Website ด้วย ASP.NET Core ในปี 2024 link: /paths/aspnet-core @@ -47,5 +56,4 @@ features: - title: Azure details: รวบรวมเนื้อหาที่เกี่ยวกับการใช้งาน Microsoft Cloud - Azure ในการพัฒนาระบบ link: /paths/azure ---- - +--- \ No newline at end of file diff --git a/paths/practices/design/design-patterns.md b/paths/practices/design/design-patterns.md new file mode 100644 index 0000000..77275a9 --- /dev/null +++ b/paths/practices/design/design-patterns.md @@ -0,0 +1,22 @@ +# Design Patterns +Design Patterns เป็นชุดของวิธีการแก้ไขปัญหาที่ใช้บ่อยในวิศวกรรมซอฟต์แวร์ ซึ่งออกแบบมาเพื่อเพิ่มความสามารถในการนำกลับมาใช้ใหม่และความยืดหยุ่นของโค้ด โดยเฉพาะในการแก้ปัญหาที่มีลักษณะเดียวกันและมีความซับซ้อน โดยมีรูปแบบการใช้งานที่เป็นไปตามหลักการที่ได้รับการยอมรับว่าเป็นวิธีการที่ดีในการแก้ปัญหาลักษณะนั้นๆ + +## ประเภทของ Design Patterns +Design Patterns ที่ออกแบบโดย Gang of Four ประกอบด้วย 23 รูปแบบ ซึ่งแบ่งออกเป็น 3 กลุ่มหลักๆ ดังนี้: + +1. **Creational Patterns**: เกี่ยวข้องกับวิธีการสร้าง object +2. **Structural Patterns**: เกี่ยวข้องกับการจัดโครงสร้าง class และ object +3. **Behavioral Patterns**: เกี่ยวข้องกับการสื่อสารและการทำงานร่วมกันระหว่าง object + +ซึ่ง patterns ทั้งหมดมีการอธิบายอย่างละเอียดรวมทั้งมีภาพประกอบในเวบไซต์ [Refactoring Guru](https://refactoring.guru/design-patterns) +แต่ถ้ายังไม่มีเวลาศึกษาทั้งหมด สามารถเริ่มต้นจาก Cheat sheet ด้านล่างนี้ได้เลย + +## Design Pattern Cheat Sheet +ผู้เขียน: คุณ [เปิ้ล](https://medium.com/@rachanee) + +ในบทความ [Design Pattern Cheat Sheet](https://medium.com/@rachanee/design-pattern-cheatsheet-8a8ea94e3777) มีสรุป Design Patterns ทั้ง 23 รูปแบบให้สั้นกระชับ เข้าใจง่าย มีรูปภาพประกอบพร้อมคำอธิบาย รวมทั้งข้อดีและข้อควรระวังของแต่ละ pattern ไว้ให้ด้วย แนะนำให้พิมพ์เก็บไว้ใช้อ้างอิงเวลาออกแบบ class และ object ได้เลย + +## ตัวอย่างการใช้ Design Patterns +- ใช้ Singleton Pattern ในการจัดการการเข้าถึงทรัพยากรที่ใช้ร่วมกัน เช่น การเชื่อมต่อฐานข้อมูล เพื่อหลีกเลี่ยงการสร้างหลาย instance ที่ไม่จำเป็น +- ใช้ Observer Pattern ในการออกแบบระบบที่ต้องการการแจ้งเตือนและการตอบสนองต่อการเปลี่ยนแปลงของสถานะ เช่น ในระบบการแจ้งเตือน (notification system) +- ใช้ Factory Method เพื่อสร้างอ็อบเจ็กต์ในลักษณะที่ทำให้การสร้างอ็อบเจ็กต์นั้นยืดหยุ่นและสามารถปรับเปลี่ยนได้ตามความต้องการ \ No newline at end of file diff --git a/paths/practices/index.md b/paths/practices/index.md new file mode 100644 index 0000000..74098cd --- /dev/null +++ b/paths/practices/index.md @@ -0,0 +1,43 @@ +--- +outline: deep +title: 'Software Development Practices' +description: กระบวนการ วิธีการ และหลักการที่ใช้ในการพัฒนาและดูแลรักษาซอฟต์แวร์ +--- + +# Software Development Practices +**Software Development Practices** คือ แนวคิด กระบวนการ วิธีการ และหลักการที่ใช้ในการพัฒนาและดูแลรักษาซอฟต์แวร์ให้มีคุณภาพสูง สามารถตอบสนองความต้องการของผู้ใช้ได้อย่างดี และช่วยให้ทีมพัฒนาทำงานได้อย่างมีประสิทธิผลเพื่อบรรลุเป้าหมายที่กำหนดไว้ + +## ความสำคัญของ Software Development Practices +1. **คุณภาพของซอฟต์แวร์**: การใช้วิธีการที่เหมาะสมช่วยให้ซอฟต์แวร์มีความเสถียร ปลอดภัย และทำงานได้ตรงตามความต้องการของผู้ใช้ +2. **ประสิทธิภาพการทำงาน**: การมีแนวทางและขั้นตอนที่ชัดเจนทำให้ทีมสามารถทำงานร่วมกันได้อย่างมีประสิทธิภาพ ลดความสับสน และช่วยให้โครงการเสร็จทันตามกำหนดเวลา +3. **การจัดการความเสี่ยง**: การใช้แนวทางปฏิบัติที่ดีช่วยในการตรวจสอบและลดความเสี่ยงที่อาจเกิดขึ้นในการพัฒนาซอฟต์แวร์ในทุกขั้นตอน ตั้งแต่การวางแผน การจัดลำดับความสำคัญ การเขียนโค้ด การทดสอบ ไปจนถึงการ deploy และ maintenance + +## ตัวอย่าง Software Development Practices +### Ways of Working +- **Agile Methodologies**: เช่น Scrum และ Kanban ที่เน้นการพัฒนาแบบ Iterative Development และการปรับปรุงอย่างต่อเนื่อง +- **Pair Programming**: การทำงานคู่กัน 2 คน(หรือมากกว่า) เพื่อเพิ่มคุณภาพและลดข้อผิดพลาด + +### Coding Practices +- **Automated Testing**: การเขียนเทสอัตโนมัติเพื่อตรวจสอบความถูกต้องแทนการใช้การทดสอบด้วยมือเพื่อทดสอบระบบอย่างต่อเนื่องและมีประสิทธิภาพ +- **Test-Driven Development (TDD)**: การเขียนเทสก่อนแล้วจึงเขียนโค้ดเพื่อให้เทสนั้นๆ ผ่าน ช่วยให้โค้ดที่เขียนมีคุณภาพและทดสอบได้ง่ายขึ้น (testable code) รวมทั้งทำให้มั่นใจว่าเทสที่เขียนสามารถจับข้อผิดพลาดได้จริง (red-green-refactor) +- **Version Control**: การใช้ระบบควบคุมเวอร์ชัน เช่น Git เพื่อจัดการการเปลี่ยนแปลงของโค้ด และทำให้ทีมสามารถทำงานร่วมกันได้ง่ายขึ้น +- **Code Refactoring**: การปรับปรุงโค้ดเพื่อเพิ่มความเข้าใจ ลดความซับซ้อน และเพิ่มประสิทธิภาพของโค้ดอย่างต่อเนื่อง +- **Code Review**: การตรวจสอบโค้ดโดยเพื่อนร่วมทีมเพื่อเพิ่มคุณภาพและลดข้อผิดพลาดก่อนการ merge โค้ดเข้า main branch + +### Design Practices +- **Design Patterns**: ใช้รูปแบบการออกแบบที่ได้รับการยอมรับว่าเป็นวิธีการที่ดีในการแก้ปัญหาที่คล้ายกัน +- **Domain-Driven Design (DDD)**: การออกแบบระบบโดยให้ความสำคัญกับโดเมนของระบบ และแบ่งระบบออกเป็นโดเมนย่อยๆ +- **Microservices Architecture**: การออกแบบระบบเป็นส่วนย่อยๆ ที่เป็นอิสระกัน ทำให้ง่ายต่อการปรับปรุง และลดความซับซ้อนของระบบ + +### Deployment Practices +- **Continuous Integration (CI)**: การ integrate โค้ดที่พัฒนาใหม่เข้าสู่ระบบหลัก หรือ main branch บ่อยๆ เพื่อตรวจสอบความถูกต้องและลดปัญหาการ merge conflicts +- **Continuous Deployment (CD)**: การ deploy โค้ดใหม่เข้าสู่ production อยู่เรื่อยๆ โดยไม่รอ deploy ที่เดียวเยอะๆ (big-bang release) เพื่อลดความเสี่ยงและช่วยให้เห็นปัญหาได้เร็วขึ้น + +### Other Practices: +- **Containerization**: การใช้เทคโนโลยี container เช่น Docker เพื่อแยกแยะและจัดการทรัพยากรอย่างมีประสิทธิภาพ +- **Infrastructure as Code (IaC)**: การจัดการ infrastructure ในรูปแบบของโค้ดซึ่งมี version control และสามารถ deploy ได้โดยอัตโนมัติ +- **Living Documentation**: การอัปเดตเอกสารประกอบการพัฒนาซอฟต์แวร์อยู่เสมอเพื่อให้สอดคล้องกับโค้ดปัจจุบันจะช่วยให้ทีมพัฒนาและผู้มีส่วนเกี่ยวข้องสามารถเข้าใจระบบและการเปลี่ยนแปลงของระบบได้ตลอดเวลา +- **Error Handling and Logging**: จัดการข้อผิดพลาดในระบบและการบันทึกข้อมูลเพื่อการ investigate ปัญหาอย่างมีประสิทธิภาพ +- **Continuous Monitoring**: การมีระบบตรวจสอบและ monitor ระบบที่มีประสิทธิภาพ เพื่อให้สามารถเห็นและแก้ปัญหาได้อย่างรวดเร็ว +- **Security Best Practices**: การปฏิบัติตามแนวทางที่ดีในเรื่องความปลอดภัยของระบบเพื่อป้องกันปัญหาด้านความปลอดภัย เช่น การโจมตีหรือการเข้าถึงข้อมูลที่ไม่เหมาะสม +- **Performance Optimization**: การปรับปรุงประสิทธิภาพของระบบเพื่อความพอใจของผู้ใช้งานและการประหยัดทรัพยากรของระบบ \ No newline at end of file diff --git a/paths/software-architecture/index.md b/paths/software-architecture/index.md new file mode 100644 index 0000000..344747e --- /dev/null +++ b/paths/software-architecture/index.md @@ -0,0 +1,13 @@ +--- +outline: deep +title: 'Software Architecture' +description: เนื้อหาและรูปแบบต่างๆของ software architecture +--- + +# Software Architecture Guideline +เนื้อหาในส่วนนี้จะอธิบายเกี่ยวกับ Software Architecture ซึ่งเป็นส่วนสำคัญส่วนหนึ่งในการสร้างและออกแบบซอฟต์แวร์ที่มีคุณภาพ + +โดยที่เนื้อหาในส่วนนี้จะมีการแนะนำและอธิบาย architectural styles และ patterns ต่างๆที่พบเจอได้โดยทั่วไปในการออกแบบ ระบบรวมถึงตัวอย่างการนำไปใช้งานจริง + +หวังว่าเนื้อหาที่แนะนำจะมีประโยชน์ต่อผู้อ่านทุกคนนะคะ + diff --git a/paths/software-architecture/introduction/software-architecture-intro.md b/paths/software-architecture/introduction/software-architecture-intro.md new file mode 100644 index 0000000..a99a19c --- /dev/null +++ b/paths/software-architecture/introduction/software-architecture-intro.md @@ -0,0 +1,12 @@ +# Software Architecture +Software Architecture หรือสถาปัตยกรรมซอฟต์แวร์ คือ **โครงสร้างและความสัมพันธ์ขององค์ประกอบต่างๆในระบบรวมถึง หลักการและแนวคิดในการออกแบบ** ที่ช่วยให้บรรลุเป้าหมายทั้งในด้านประสิทธิภาพ คุณภาพ การบริหารจัดการ และการบำรุงรักษาที่คาดหวัง เพื่อช่วย**ให้ทุกฝ่ายเข้าใจภาพรวมของระบบและสื่อสารการตัดสินใจที่มีร่วมกัน** + +ในปัจจุบัน มีรูปแบบของ architecture ที่ใช้งานกันแพร่หลาย เช่น **microservices** หรือ **monolith** โดยรูปแบบต่างๆนี้เรียกว่า **architectural styles** โดยการออกแบบ software architecture อาจเป็นการผสมผสาน architectural styles และ principles ต่างๆเข้ามาไว้ด้วยกันเพื่อสร้างระบบที่มีความยืดหยุ่นและสามารถตอบโจทย์กลยุทธ์ขององค์กรได้ตามความเหมาะสม โดยพิจารณาจากประโยชน์ ข้อดี ข้อเสีย รวมถึงข้อจำกัดต่างๆที่มี + +Software Architecture ที่ดีจะต้อง**ไม่ใช่สิ่งตายตัว ไม่ได้ออกแบบมาครั้งเดียวแต่จะต้องสามารถเปลี่ยนแปลงและวิวัฒนาการได้**ตามกลยุทธ์และทิศทางของโครงการหรือองค์กรที่มักจะเปลี่ยนแปลงไปตามกาลเวลา การมี software architecture ที่ดีจะเป็นส่วนสำคัญในการสร้างซอฟต์แวร์ที่มีคุณภาพและตอบโจทย์ความต้องการในด้านต่างๆ ทั้งในเชิงกลยุทธ์ การพัฒนาผลิตภัณฑ์ การทดสอบ รวมถึงการบริหารจัดการ และที่สำคัญที่สุดคือผู้ใช้งานหรือลูกค้า ซึ่งจะช่วยสร้างความสามารถในการแข่งขันให้กับองค์กรได้ +## ความสำคัญของ Software Architecture +Software Architecture เปรียบเสมือน blueprint ที่ใช้สื่อสารแนวทางการตัดสินใจที่มีร่วมกันระหว่างผู้มีส่วนได้ส่วนเสีย ซึ่งอาจจะเป็นลูกค้า ผู้บริหารโครงการ ทีมพัฒนาผลิตภัณฑ์ และรวมถึงฝ่ายบริหารต่างๆด้วย โดยมักจะถูกใช้ในการสื่อสารเกี่ยวกับทิศทาง กลยุทธ์ และแนวคิดต่างๆเพื่อให้เกิดความเข้าใจในทิศทางเดียวกัน + +โดย software architecture ช่วยให้ผู้ที่เกี่ยวข้องสามารถ**วิเคราะห์ และประเมินความเสี่ยงต่างๆ เช่น ความเสี่ยงด้านความปลอดภัย ด้านงบประมาณ ด้านเวลา รวมถึงความสอดคล้องกับทิศทางและกลยุทธขององค์กร**ก่อนที่จะลงมือสร้างผลิตภัณฑ์ออกมาอีกด้วย โดย software architecture ที่ดีจะช่วยให้ทุกฝ่ายเข้าใจระบบในทิศทางเดียวกัน ทั้งในด้านการพัฒนา การจัดการ การทดสอบ และการพัฒนาต่อยอดในอนาคตอีกด้วย เช่นการเพิ่มความสามารถใหม่ๆ หรือการ scale ระบบให้รองรับผู้ใช้งานที่มากขึ้น + +โดยในช่วงเวลาหลังๆ นอกจากจะเป็นเครื่องมือที่ใช้ในการสื่อสารเกี่ยวกับการออกแบบระบบแล้ว software architecture ได้เข้ามามีบทบาทสำคัญในการออกแบบทีมในองค์กรสมัยใหม่ โดยมีแนวคิดต่างๆ เช่นการปรับเปลี่ยนรูปแบบทีมให้เข้ากับ architecture ที่ต้องการ ซึ่งช่วยให้ทีมต่างๆสามารถทำงานร่วมกันได้อย่างมีประสิทธิภาพและช่วยลดคอขวดในการสื่อสารและการดำเนินงาน หากสนใจหัวข้อดังกล่าว ขอแนะนำหนังสือ [Team Topologies](https://teamtopologies.com/) เขียนโดยคุณ Ruth Malan ค่ะ \ No newline at end of file diff --git a/paths/software-architecture/introduction/styles-n-patterns.md b/paths/software-architecture/introduction/styles-n-patterns.md new file mode 100644 index 0000000..1d11b8a --- /dev/null +++ b/paths/software-architecture/introduction/styles-n-patterns.md @@ -0,0 +1,54 @@ +# Architectural Styles & Patterns +ในโลกของการพัฒนาซอฟต์แวร์ การออกแบบ architecture ถือเป็นหนึ่งในขั้นตอนที่สำคัญที่สุด เพราะ software architecture นั้น**ทำหน้าที่เหมือน blueprint ที่กำหนดโครงสร้างและการทำงานของระบบทั้งหมด** การเลือกใช้ architectural styles และ patterns ที่เหมาะสมจะช่วยให้ระบบมีความยืดหยุ่น บำรุงรักษาง่าย และสามารถรองรับการเปลี่ยนแปลงในอนาคตได้ + +หากไม่มีการกำหนดรูปแบบของ architecture ที่ดีตั้งแต่เริ่มต้น อาจทำให้เกิดความไม่สอดคล้องกันในการพัฒนาได้ ซึ่งมีความเสี่ยงหลายอย่าง เช่น เกิด coupling ระหว่าง dependencies จำนวนมาก หรือมีความซับซ้อนเกืนความจำเป็น ซึ่งส่งผลให้การบริหารจัดการ และการพัฒนาต่อยอดในอนาคตทำได้ยากมากขึ้น + +โดยเนื้อหาเกี่ยวกับ architectural styles และ patterns ที่พบได้บ่อยในปัจจุบันได้จากหัวข้อด้านล่างนี้เลยค่ะ +## Architectural Styles +Architectural Styles เป็นการอธิบายภาพใหญ่ของ architecture โดย**ให้ความสำคัญกับภาพรวมของระบบที่กำลังออกแบบมากกว่ารายละเอียดในการ implementation เชิงลึก** โดยอาจละเว้นในส่วนของ technologies หรือเครื่องมือที่จะใช้กับองค์ประกอบนั้นๆ แต่จะเป็นการอธิบายภาพรวมและคุณลักษณะขององค์ประกอบนั้นๆ เช่น + +- ระบบย่อยต่างๆ +- ระบบภายนอก +- รูปแบบการเชื่อมต่อระหว่างองค์ประกอบต่างๆ +- การจัดการข้อมูลในระบบ +- กลไกความปลอดภัย +- Layers + +โดย architectural styles ที่เรามักพบเห็นได้บ่อยในปัจจุบัน มีดังนี้ +- **Monolithic Architecture:** เป็นรูปแบบของ architecture ที่ทุกส่วนประกอบของ application เช่น user interface, business logic, หรือ data access ถูกรวมเข้าไว้ใน codebase เดียวกัน โดย application ที่เป็น monolith จะถูกพัฒนา ทดสอบ และบริหารจัดการอยู่ใน package เดียวกัน +- **Microservices Architecture:** เป็นรูปแบบของ architecture ที่ application จะถูกแยกออกไป service ย่อยๆหลายตัว โดยที่แต่ละ service จะทำหน้าที่เฉพาะงานที่เกี่ยวข้องกับตัวเอง และเป็นอิสระจาก service อื่นๆในการพัฒนาและบริหารจัดการ ทำให้มีความคล่องตัวในการพัฒนาและยืดหยุ่นในการเลือกใช้เทคโนโลยี +- **Event-Driven Architecture:** เป็นรูปแบบ architecture ที่ใช้ event ในการสื่อสารระหว่าง service ต่างๆในระบบ โดย events ต่างๆจะแสดงถึงการเปลี่ยนแปลงของข้อมูล หรือเหตุการณ์ต่างๆที่เกิดขึ้นในระบบ รูปแบบ architecture นี้มักจะมีตัวกลางในการรับส่ง events เรียกว่า message broker หรือ event broker +- **Client-Server:** เป็นรูปแบบของ architecture ที่ใช้งานกันแพร่หลาย โดย client หรือผู้ใช้งานจะร้องขอการประมวลผลหรือการทำงานไปที่ server ซึ่งทำหน้าที่ประมวลผลและส่งผลลัพธ์กลับไปที่ client หนึ่งในลักษณะการใช้งานที่พบได้บ่อยที่สุดในปัจจุบันคือ web server ที่ให้บริการ website หรือ web service ต่างๆ + +นอกจากนี้ยังมี architectural styles อื่นๆอีกมากมาย โดยสามารถอ่านเพิ่มเติมจาก [บทความของทาง JavatPoint](https://www.javatpoint.com/architectural-styles-in-software-engineering) โดยในบทความนี้จะเปรียบเทียบข้อดีข้อเสียของแต่ละ architectural styles ให้เห็นด้วยค่ะ +## Architectural Patterns +Architectural Patterns คือแนวทางการและวิธีการต่างๆที่ใช้เพื่อ**แก้ปัญหาที่มักเกิดขึ้นซ้ำๆในการออกแบบ software architecture** เช่น + +- การจัดการกับความซับซ้อน +- ประสิทธิภาพของระบบ +- Scalability +- การ test ระบบ +- การ reuse ส่วนประกอบต่างๆในระบบ + +โดยตัวอย่างของ architectural patterns มีดังนี้ + +- **MVC (Model-View-Controller):** เป็นการแบ่ง software ออกเป็นสามส่วนหลักๆ ได้แก่ models ที่ทำหน้าทีจัดการกับข้อมูล, views ที่ทำหน้าที่ติดต่อกับผู้ใช้ในการแสดงผลและรับคำสั่งต่างๆ, และ controllers ที่ทำหน้าที่ในการประมวลผลต่างๆและเชื่อมโยง views กับ models เข้าด้วยกัน +- **Event Sourcing:** เป็นรูปแบบของ architecture ที่ทุกการเปลี่ยนแปลงของข้อมูลต่างๆใน application เช่น entity หรือ aggregate จะถูกเก็บในลักษณะของ **ลำดับของเหตุการณ์ (sequence of events)** โดยจะเป็นการจัดการข้อมูลในลักษณะ append-only ทำให้สามารถตรวจสอบทุกการเปลี่ยนแปลงของข้อมูลและสามารถนำ events เหล่านี้ไปประมวลผลเพิ่มเติมได้ โดยมักจะใช้คู่กับ pattern แบบ CQRS +- **Command Query Responsibility Segregation (CQRS):** เป็นการแยก application ออกเป็นสองส่วน ได้แก่ ส่วนที่เป็น command ทำหน้าที่เปลี่ยนแปลงข้อมูล และส่วนที่เป็น query ทำหน้าที่เรียกดูข้อมูล โดยทั้งสองส่วนจะใช้ model แยกจากกันชัดเจน ในบางระบบอาจแยกฐานข้อมูล และ process ในการทำงานออกจากกันเพื่อประสิทธิภาพสูงสุด +- **Saga Pattern:** เป็นรูปแบบของการจัดการ long-running transactions ผ่านระบบต่างๆ โดยมักจะถูกใช้ใน architecture ที่เป็น distributed systems เช่น microservices โดยแต่ละ microservice จะทำการประมวลผลและส่งต่อไปยัง microservice ถัดไป + +## ความแตกต่างระหว่าง Architectural Styles และ Patterns +หลายครั้งที่ architectural styles และ patterns มีความแตกต่างกันในมุมมองของ architecture โดยที่ **architectural styles จะเน้นการอธิบายองค์ประกอบของระบบในภาพกว้าง**โดยไม่ลงลึกในรายละเอียดและ tools หรือ frameworks ที่ใช้การพัฒนา โดยส่วนใหญ่แล้วมักจะเป็นการกำหนดรูปร่างของ architecture ของระบบ เช่น จำนวน service ต่างๆ ระบบภายนอก และลักษณะการเชื่อมต่อขององค์ประกอบต่างๆ + +ในขณะที่ **architectural patterns จะเป็นการมองในระดับที่เล็กลงมาและมีรายละเอียดมากขึ้น** โดยเน้นไปที่การแก้ปัญหาเฉพาะจุดของระบบ เช่น รูปแบบในการเก็บข้อมูล รูปแบบในการจัดวาง modules ในระบบ หรือรูปแบบในการรับ-ส่งข้อมูลที่มีความเฉพาะเจาะจงมากขึ้น + +ตัวอย่างเช่น การออกแบบระบบโดยใช้ architectural style แบบ microservice และ event-driven architecture อาจเลือกใช้ architectural pattern แบบ pub/sub โดยมี message broker เป็นตัวกลางเพื่อรับ-ส่งข้อมูลระหว่าง module ต่างๆ + +ตารางด้านล่างเป็นการเปรียบเทียบโดยคร่าวๆระหว่าง architectural styles และ patterns + +| Feature | Architectural Styles | Architectural Patterns | +| ----------------------- | --------------------------- | ------------------------ | +| **Level of Abstraction** | ภาพกว้าง | มีรายละเอียดมากขึ้น | +| **Scope** | รวมของระบบทั้งหมด | การแก้ปัญหาเฉพาะส่วน | +| **Focus** | องค์ประกอบและความสัมพันธ์ต่างๆ | รูปแบบ แนวทางในการแก้ปัญหา | +| **Examples** | Event-Driven Architecture | Pub/Sub, Event Stream Processing, CQRS | diff --git a/paths/typescript/cookbook/append-prefix-object-property-key.md b/paths/typescript/cookbook/append-prefix-object-property-key.md new file mode 100644 index 0000000..04587c7 --- /dev/null +++ b/paths/typescript/cookbook/append-prefix-object-property-key.md @@ -0,0 +1,40 @@ +# เพิ่ม Prefix ในแต่ละ Property Key ใน Object + +Ref: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-1.html#key-remapping-in-mapped-types + +Require: Version 4.1 + +```ts + +type AppendPrefix = { + [K in keyof T as `${U}${K & string}`]: T[K] +}; + +function appendPrefix, U extends string>(obj: T, prefix: U) { + const result: Partial> = {}; + for (const [key, value] of Object.entries(obj)) { + result[`${prefix}${key}`] = value; + } + return result; +} + +interface Person { + name: string; + age: number; + location: string; +} + +const person: Person = { + name: 'John', + age: 30, + location: 'Thailand' +} + +const oldPerson = appendPrefix(person, 'old_'); + +oldPerson.old_age; +oldPerson.old_location; +oldPerson.old_name; + +oldPerson.name; // ❌ Property 'name' does not exist on type 'Partial>' +``` \ No newline at end of file diff --git a/paths/typescript/cookbook/create-own-enum-string.md b/paths/typescript/cookbook/create-own-enum-string.md new file mode 100644 index 0000000..f3601c2 --- /dev/null +++ b/paths/typescript/cookbook/create-own-enum-string.md @@ -0,0 +1,35 @@ +# สร้าง enum ใช้เอง แบบที่ Key กับ Value เหมือกัน + +```ts +type TupleToObject = { + [K in T[number]]: K +} + +type Enum = T[keyof T]; + +const state = ['todo', 'in progress', 'completed'] as const; + +// What we want +// const stateA = { +// todo: 'todo', +// 'in progress': 'in progress', +// completed: 'completed' +// } + +function tupleToObject(data: T): TupleToObject{ + return data.reduce((result: any, v: keyof any) => { + result[v] = v; + return result; + }, {}); +} + +const GenState = tupleToObject(state); +console.log(GenState); + +const myState1: Enum = GenState.completed; +const myState2: Enum = 'completed'; +const myState3: Enum = 'hold'; // ❌ Error: Type '"hold"' is not assignable to type 'EnumGenState'. + +// or +const myState4: keyof typeof GenState = 'completed'; +``` \ No newline at end of file diff --git a/paths/typescript/cookbook/create-union-type-from-array.md b/paths/typescript/cookbook/create-union-type-from-array.md new file mode 100644 index 0000000..df6e9eb --- /dev/null +++ b/paths/typescript/cookbook/create-union-type-from-array.md @@ -0,0 +1,11 @@ +# สร้าง a Union type from an Array in TypeScript + +```typescript +// 👇️ const sizes: readonly ["small", "medium", "large"] +const sizes = ['small', 'medium', 'large'] as const; + +// 👇️ type SizesUnion = "small" | "medium" | "large" +type SizesUnion = typeof sizes[number]; +``` + +https://bobbyhadz.com/blog/typescript-create-union-type-from-array \ No newline at end of file diff --git a/paths/typescript/cookbook/filter-some-property-of-record.md b/paths/typescript/cookbook/filter-some-property-of-record.md new file mode 100644 index 0000000..a6b2e40 --- /dev/null +++ b/paths/typescript/cookbook/filter-some-property-of-record.md @@ -0,0 +1,36 @@ +# เลือกบาง Property จาก Record + +Ref: https://rossbulat.medium.com/typescript-typing-dynamic-objects-and-subsets-with-generics-44ba3988a91a + +## มาดูตัวอย่างเต็มๆ กันดีกว่า + +```typescript +/** + * Get only some keys and return the correct key + */ +export function pick(obj: T, keys: U[]) { + // สาเหตุที่เลือกใช้ Partial ของ Pick จะทำให้สร้าง Object ว่างๆ ได้นั่นเอง แล้วค่อยเลือก Property ทีหลัง + const result: Partial> = {}; + keys.forEach((key) => { + if (typeof obj[key] !== "undefined") { + result[key] = obj[key]; + } + }); + return result; +} + +interface IPost { + id: string; + title: string; +} + +const post: IPost = { + id: '1', + title: 'Go Lang' +} +const result = pick(post, ['id']); + + +console.log(result.id); +console.log(result.title); // ❌ Error: Property 'title' does not exist on type 'Partial>' +``` \ No newline at end of file diff --git a/paths/typescript/cookbook/type-safe-builder-pattern.md b/paths/typescript/cookbook/type-safe-builder-pattern.md new file mode 100644 index 0000000..a6ff726 --- /dev/null +++ b/paths/typescript/cookbook/type-safe-builder-pattern.md @@ -0,0 +1,84 @@ +# สร้าง Builder Pattern แบบ Type-Safe + +## Basic + +```typescript +class Inventory = {}> { + + items: Items = {} as Items; + + add>(value: NewItem) { + this.items = { + ...value as unknown as Items + } + return this as Inventory; + } +} + +const inventory = new Inventory() + .add({ + hello: 'world', + }).add({ + typescript: 5.1, + numbers: [23, '123'] + }); + +console.log(inventory.items.typescript) + + +type A = typeof inventory.items; + +// type A = { +// hello: string; +// } & { +// typescript: number; +// numbers: (string | number)[]; +// } +``` + +[playground](https://www.typescriptlang.org/play?#code/FAYwNghgzlAECSA7AbgU0QFwPYCcCeAPPBqgLZyoAeJiAJnAEqoi60FQY4CWiA5gDSwArogDWiLAHdEAPlgBeWAG8AvnKXBgsbbC4lyALgT64i1bGjGyUANyadF2mwByqScTKwqNerCYscNg5uPkERcSlZGQAKZAgwIVQjV3d9AEplLQcdDAALLigAOj1rBUzsithC6riE1As4cIlpBqtyLIqVDuycVAwhHERYPILWpDRMXEIPclgAMlgUmZk7By6u0CxEDl0UdGx8MsQ3BD3J-Gi07sKIJ2iNSthc1DAwLCMAcklcMFoP-m6KjSNzuD0qGDwAAdUFAQNxIRgjABWQoARgBj0QQlIACNUDgoEYANoAJgAzIIPqjyR8ALqAtJ2TbbLBgVCFN68aI8CYHPDFEyFCHQ2HwjBXTTC+oAQTKUqwADNdrypgLrHYAPQanQAPQA-EA) + +Ref: TypeScript Meetup Thailand July 2023 +https://www.facebook.com/phantipk/videos/289991566938840?idorvanity=1377224595918442 + +## More advanced + +```typescript +class ObjectBuilder = {}> { + + constructor(private readonly jsonObject: Items) { } + + add(key: K, value: V) { + const nextPart = { [key]: value } as Record; + return new ObjectBuilder({ ...this.jsonObject, ...nextPart }) as + ObjectBuilder<{ [Key in keyof (Items & Record)]: (Items & Record)[Key] }>; + } + + build(): Items { + return this.jsonObject; + } + + static create(){ + return new ObjectBuilder({}); + } + +} + +const json = ObjectBuilder.create() +.add('aString', 'some text') +.add('aNumber', 2) +.add('anArray', [1, 2, 3]) +.build(); + +type B = typeof json; + +// type B = { +// aString: string; +// aNumber: number; +// anArray: number[]; +// } +``` + +[playground](https://www.typescriptlang.org/play?#code/MYGwhgzhAEDyBGArApsALgIQK4EsQBNkAnAHgEk1kBbGZAD0oDt8YAlVAeyPxIjSJyMA5gBpoWRgGtGHAO6MAfNAC80AN4BfJWoBQO6AejAOjPkSzouACgAOAgG5hK0IsjD4TIAJ7REEEwgo6ABc0BTUEACU6tAaeobQ7jwA0tD0TCzQZoKi0ABqClaSyF6hyWKOIFjIoXnRugkJxqZo0IzpAApgRK2qatAA2sVeALqhldWxiWyc3CTl+QoA3PqNBq5oWESMbciycEiomLgExFb9AHRXaAAWOBAXfgGH6GJXF+0MXT2x0ZCrawSgSO2DwhFI-QGyRK0EE0GGHAAZtArOEaNAAGTQdjGOYLAqRMYotEwLE4rgpMQEqElEaxZYA2LxQzwE74KyRUIk9SMhIbLY7W73R7+RjA9ArBJxRl8Jw4YBGVxOZAchqA-nbXb7cXHMFnTSRSWGaXS5p8XyilQHIK605EC7AJWUDk6C5JKwAcjAAGV+DkPWIPf4qMhoJQGB7Iq73V6AHJYKjwYgB6AAJijbvw7K9jAAgkQiGAvCmBgBGMSpsQAZhGGdZYI5Kx0aC8NlDGCtLbbSItJibAHp+2HW+2rbpB40fX7hKFssIVhOEmB44niKFGAmk0QF0Ol3mC0X15viAMRjumXogA) + +Ref: https://medium.hexlabs.io/the-builder-pattern-with-typescript-using-advanced-types-e05a03ffc36e \ No newline at end of file diff --git a/paths/typescript/cookbook/zod-typescript-integration.md b/paths/typescript/cookbook/zod-typescript-integration.md new file mode 100644 index 0000000..2c83aa2 --- /dev/null +++ b/paths/typescript/cookbook/zod-typescript-integration.md @@ -0,0 +1,61 @@ +# Data Validation ด้วย Zod ใช้คู่กับ TypeScript ได้ + +หลายคนๆ อาจจะรู้จักกับ `Zod` ในการทำ Data Validation + +> TypeScript-first schema validation with static type inference + +## การเขียน Utility Function เพื่อทำ Data Validation + +โดยใน [Doc ของ zod](https://github.com/colinhacks/zod/blob/c617ad3edbaf42971485f00042751771c335f9aa/README.md#writing-generic-functions) พูดถึงการเขียน Function Generics สำหรับทำ Validation แต่ไม่ได้บอก Return Type ให้ เราสามารถใข้ `z.infer` ได้เพื่อระบุ Type ของ Schema ได้ + +```ts +import { z, ZodFormattedError } from 'zod'; + +export function parseEnv(schema: T, input: Record): z.infer { + const _functionEnv = schema.safeParse(input); + + if (_functionEnv.success === false) { + const message = 'Invalid input'; + throw new Error(zodPrettyError(message, _functionEnv.error.format())); + } + + return { ..._functionEnv.data }; +} + +export const zodPrettyError = (message: string, errors: ZodFormattedError, string>) => { + const ouput = Object.entries(errors) + .map(([name, value]) => { + if (value && '_errors' in value) return `${name}: ${value._errors.join(', ')}\n`; + }) + .filter(Boolean); + + return `❌ ${message}:\n ${ouput.join(', ')}`; +}; + +// +// ตัวอย่างการใช้งาน +// + +const appSchema = z.object({ + Webhook_URL: z.string().url(), + Is_Enable: z.boolean(), +}); + +type App = t.infer; +// จะได้ Type ของ TypeScript ออกมา +// type App = { +// Webhook_URL: string; +// Is_Enable: boolean; +// } + +const appData: App = { + Webhook_URL: 'https://typescrip-th.thadaw.com', + Is_Enable: true, +} + +// Validate it! +const parsingData = parseEnv(appSchema, data); + +parsingData.Webhook_URL // type => string +parsingData.Is_Enable // type => boolean +``` \ No newline at end of file diff --git a/paths/typescript/everyday-types/catch-error.md b/paths/typescript/everyday-types/catch-error.md new file mode 100644 index 0000000..e9bc987 --- /dev/null +++ b/paths/typescript/everyday-types/catch-error.md @@ -0,0 +1,59 @@ +--- +outline: deep +--- +# จัดการ Try-Catch Error + +Because JS can throw any type: + +```ts +throw 'What the!?' +throw 5 +throw {error: 'is this'} +throw null +throw new Promise(() => {}) +throw undefined +``` + +ดังนั้นเราสามารถใช้ Type Narrowing ในการจัดการ Type ได้อย่างถูกต้อง +```ts +try { + throw new Error('Oh no!'); +} catch (error) { + // error always unknown type + if (error instanceof Error) return error.message; + return String(error); +} +``` + +ตัวอย่างการใช้งานจริง ด้วย [zod](https://github.com/colinhacks/zod) +```ts +import { z } from 'zod'; + +const errorWithMessageSchema = z.object({ + message: z.string(), +}) + +type ErrorWithMessage = z.infer; + +function isErrorWithMessage(error: unknown): error is ErrorWithMessage { + return errorWithMessageSchema.safeParse(error).success; +} + +function toErrorWithMessage(maybeError: unknown): ErrorWithMessage { + if (isErrorWithMessage(maybeError)) return maybeError + + try { + return new Error(JSON.stringify(maybeError)) + } catch { + // fallback in case there's an error stringifying the maybeError + // like with circular references for example. + return new Error(String(maybeError)) + } +} + +function getErrorMessage(error: unknown) { + return toErrorWithMessage(error).message +} +``` + +Ref: https://kentcdodds.com/blog/get-a-catch-block-error-message-with-typescript \ No newline at end of file diff --git a/paths/typescript/everyday-types/object-manipulation.md b/paths/typescript/everyday-types/object-manipulation.md new file mode 100644 index 0000000..f37efd1 --- /dev/null +++ b/paths/typescript/everyday-types/object-manipulation.md @@ -0,0 +1,84 @@ +--- +outline: deep +--- +# การจัดการกับ Object + +บางครั้งเราต้องการเพิ่มคุณสมบัติใหม่ให้กับวัตถุ แต่การกระทำนี้อาจส่งผลให้เกิดข้อผิดพลาด ดูตัวอย่างด้านล่าง: + +```ts +const nameRecord = {}; +nameRecord.john = 1; // ❌ Error: Property 'john' does not exist on type '{}'. +``` + +เราไม่สามารถเพิ่มคุณสมบัติ 'john' ให้กับ `nameRecord` ได้เนื่องจากวัตถุไม่มีคุณสมบัตินั้น + +ในการแก้ปัญหานี้ เราต้องใช้ [Mapped Types](../handbook/mapped-types) ในกรณีนี้คือ `Record` + +## Record + +`Record` ช่วยให้เราสามารถสร้างวัตถุที่สามารถเพิ่มคุณสมบัติใหม่ๆ ได้ ดูตัวอย่างด้านล่าง: + +```typescript +const nameRecord: Record = { + john: 1, + micky: 2, +}; + +nameRecord['Lilly'] = 5; // เราสามารถเพิ่มคุณสมบัติใหม่ให้กับวัตถุได้ เพราะ 'Lilly' เป็นชนิดข้อมูล string + +nameRecord[true] = 5; // ❌ ข้อผิดพลาด: ไม่สามารถใช้ชนิดข้อมูล 'true' เป็นคีย์ +``` + +ในตัวอย่างนี้เราสร้างตัวแปร `nameRecord` ที่มีชนิดข้อมูล `Record` ซึ่งอนุญาตให้เพิ่มคุณสมบัติที่มีชนิดข้อมูลเป็น `string` และค่าของคุณสมบัติเป็นชนิดข้อมูล `number` นอกจากนี้เรายังไม่สามารถใช้ค่าที่ไม่ใช่ `string` เป็นชนิดข้อมูลของคีย์ได้ + +### Record with Type Union (จำเป็นต้องมีคีย์ครบทั้งหมด) + +`Record` ต้องการให้กำหนดคีย์ของ Object ให้ครบถ้วน แต่ถ้าต้องการให้เป็นแบบ Partial (ไม่จำเป็นต้องมีคีย์ครบทั้งหมด) [ตอนนี้ TypeScript ยังไม่ได้ support](https://github.com/microsoft/TypeScript/issues/43918) + +```typescript +type Activities = 'Running' | 'Walking' | 'Swimming'; +const countActivities: Record = { + Running: 0, + Swimming: 1, + Walking: 5 +} + +// คีย์ของ Object `countActivities` ต้องตรงกับชนิด `Activities` +``` + +ในตัวอย่างนี้ เรากำหนดชนิด `Activities` +ที่ประกอบด้วยค่า 'Running', 'Walking', และ 'Swimming' แล้วเราสร้าง Object `countActivities` +ที่มีชนิดข้อมูล `Record` +สิ่งนี้หมายความว่าวัตถุ `countActivities` +ต้องมีคีย์ของคุณสมบัติตรงกับที่กำหนดในชนิด `Activities` +ดังนั้นเราจึงต้องกำหนดค่าให้กับคุณสมบัติ 'Running', 'Walking', และ 'Swimming' ทั้งสามคุณสมบัติเสมอ + +### Record with Type Union (ไม่จำเป็นต้องมีคีย์ครบทั้งหมด) + +ในกรณีที่ต้องการให้คุณสมบัติทั้งหมดเป็นตัวเลือก คุณสามารถใช้ `Partial` ร่วมกับ `Record` ดังตัวอย่าง: + +```ts +type Activities = 'Running' | 'Walking' | 'Swimming'; +const countActivities2: Partial> = { + Running: 0, +} + +// คีย์ของ Object `countActivities2` ต้องตรงกับชนิด `Activities` และไม่จำเป็นต้องใส่ครบ +``` + +หรือคุณสามารถกำหนด **`PartialRecord`** เพื่อใช้งานเอง: + +```typescript +// กำหนด PartialRecord เพื่อใช้งานเอง +type PartialRecord = Partial> + +type Activities = 'Running' | 'Walking' | 'Swimming'; +const countActivities: PartialRecord = { + Running: 0, +} + +countActivities.Walking = 5; // ใช้งานได้! +countActivities.Cycling = 4; // ❌ ข้อผิดพลาด: คุณสมบัติ 'Cycling' ไม่มีอยู่ในชนิด 'Partial>' +``` + +ในตัวอย่างนี้ เรากำหนดชนิด **`Activities`** ที่ประกอบด้วยค่า 'Running', 'Walking', และ 'Swimming' และสร้าง Object **`countActivities`** ที่มีชนิดข้อมูล **`PartialRecord`** ซึ่งอนุญาตให้คุณสมบัติทั้งหมดใน Object เป็นตัวเลือก ไม่จำเป็นต้องใส่ครบทุกคีย์ แต่คีย์ที่ใช้ยังคงต้องอยู่ในชนิด **`Activities`** diff --git a/paths/typescript/everyday-types/runtime-type.md b/paths/typescript/everyday-types/runtime-type.md new file mode 100644 index 0000000..73fc5e3 --- /dev/null +++ b/paths/typescript/everyday-types/runtime-type.md @@ -0,0 +1,61 @@ +--- +outline: deep +--- +# จัดการ Runtime Type + +เมื่อเราต้องจัดการกับชนิดข้อมูลในขณะรันโปรแกรม (Runtime Type) และไม่แน่ใจว่าจะได้รับค่าอะไรเข้ามา เช่น การเรียก API ต่าง ๆ ซึ่งเราสามารถใช้ [Zod](https://github.com/colinhacks/zod) ในการตรวจสอบและกำหนด Schema ของข้อมูลในขณะรันโปรแกรม เพื่อให้ตรงตามที่เราต้องการ + +กำหนด Schema ของข้อมูลที่ต้องการ ดังนี้: + +```ts +import { z } from 'zod'; + +// นิยาม Schema ที่ต้องการ +export const responseBodySchema = z.object({ + userId: z.number(), + id: z.number(), + title: z.string(), + completed: z.boolean(), +}); +``` + +หลังจากที่เรานิยาม schema ไปแล้ว ให้ใช้คำสั่ง `safeParse` เพื่อตรวจสอบและแปลงข้อมูลในขณะรันโปรแกรม นำไปใช้งานในโค้ดได้เลย: + +```ts +const url = 'https://jsonplaceholder.typicode.com/todos/1'; +export async function getUserData() { + const rawBody = await (await fetch(url)).json(); + // เอา data ที่เป็น any type ไป validate โดยใช้ zod schema + const parseBody = responseBodySchema.safeParse(rawBody); + if (!parseBody.success) { + const { error } = parseBody; + throw new Error(`Schema is not correct, ${JSON.stringify(error.errors)}`); + } + + return parseBody.data; + // ^? { userId: number; id: number; title: string; completed: boolean; } +} +``` + +[Zod](https://github.com/colinhacks/zod) เป็นพระเอกของเราในงานนี้เลย เพราะนอกจากจะเช็ค Schema ให้ตรงกับที่เราต้องการมั้ย + +เราสามารถแปลงเป็น Type ของ TypeScript ให้เราไปทำอย่างอื่นได้ต่อ อีก ดีงามมากกกก 😍 + +```ts +type Todo = z.infer; +// ^? type Todo = { userId: number; id: number; title: string; completed: boolean; } + +const myTodo: Todo = { + userId: 1, + id: 1, + title: 'delectus aut autem', + completed: false, +}; +``` + +ใครไม่อยากใช้ zod เพียวๆ ก็มีคนทำ library เอาไว้ใช้ +- [zodios](https://github.com/ecyrbe/zodios) = zod + axios +- [zod-fetch](https://github.com/mattpocock/zod-fetch) = zod + fetch + +ลองเล่น [Playground](https://www.typescriptlang.org/play?#code/PTAEAsBdIBwZwFwgOYEtLgK4CMB0BjAewFtgBTfATwCdszgAvQgE1ULlAF5QnnQBqUAEMAHmzgAoEBGjwkwNBhwESwYkNmEi+ANaMWAWgBmZSPnBceLAaBNnwEqWFQA7ZmRG5Ik1MRiFqSFAAbx5QAF9bahJQAHJeWIBuRw9-QNAiFzgg6jI4fyyyACEWSgBlczJ1SwZcQmwAKwpIAApgiVBQTDgyagBJZgQeXBdMYjpqFoBKABoO0FRB4dHx3um5zsh0ABsyIdrs6ldkdfmiP13IMiXa7EJCXaEXU-Cp5IlM7K7qbctYqFgiBADTghBcMG2QnwZHAD3c1C8lBgqCI7hUpEgLHYwAAjEkJKkAkEhHBKC58LZMOStmDQMhTABVHrUAAiGiE0xCZzBX2oQgA7iVmJRLAKhOhQC0xRK7OYWpgflMprgQWDpslOp8gjAhNQekKRdxcvkecVShVwFUhLg4EITAAFXU9Fp8wWlN7zVBGSUAQh1erNwptmHw0LgcCmXM6mp5QVCvWi1Ailn9+tKGujGGi-NALjIOYAotREy0AAYWq0LDguQhBIjF5ozUAAEmCACkygB5AByNsgRxcaCMlBaCYCuDHeqm4VLHs64UcnVykAVLlAqcDlFwzHZyQXTlABiPx5Pp7P54vl6v15vt7v96vjmkrncniu2S8Pj8RNA76CRmiYhQAAIiEAA3IRgOSZ9v3SUIGGTACYmA3goIPXw0jjOlGWZNlICEJtjQKNNhQrapIiQoDgNwYAXw8NCJEgJEyFAAAVLEalwVwTGoAAeJiYDIQhvSI00DTIoQAD5oLAaMAD0AH5HC1UBiEodjmEIIYNMISx2k6bpegGIYcQ2BYllM+YtkgXYhlidxdnwFcOCETBiTcqpYjM84IVMa4hiMIRth6OZwneP8WliP8vOEUlyUlSAhieShI04SSowyWMulw9lRX5cUgnpSAmV6PCOTnUBgAAKiq+ZQCq0BAAE4QAiOEASThQEAEThABQ4VrAEE4QBSOFawByOFAQBCOEAVjg2OYsbACY4HqhuauqauAbksgeMhcG2QgTlLQzWXZXA9uMltgj2srDuZAYZwqz51s27ayzOg6fMufyTqe-D0V8q5mGu95Nm3MgyBgAsAEdMCClo1J0psPqED1XkSIA) + diff --git a/paths/typescript/everyday-types/type-assertions.md b/paths/typescript/everyday-types/type-assertions.md new file mode 100644 index 0000000..457a495 --- /dev/null +++ b/paths/typescript/everyday-types/type-assertions.md @@ -0,0 +1,78 @@ +--- +outline: deep +--- + +# Type Assertions + +รู้มั้ย Typescript สามารถหลอก Type ได้ + +TypeScript เป็นภาษาที่แยกตัวออกจากโลกของ JavaScript อย่างชัดเจน +สิ่งที่ TypeScript พยายามทำคือ Type inference (คาดเดา) Type จากภาษา JavaScript. +ดังนั้นเราเขียน TypeScript, แต่ ณ Runtime เราทำงานด้วย JavaScript. + +แต่ในบางครั้งเรามักจะมีการใช้ any โดยตั้งใจ หรือไม่ตั้งใจ ก็ตาม +แต่ any สามารถบังคับให้ Type เป็นอะไรก็ได้ผ่าน return type ของ function, as, หรือ satisfies + +แนวทางการป้องกันความเข้าใจผิดเราอาจจะใช้ Eslint ที่ชื่อ +[@typescript-eslint/no-unsafe-return](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-unsafe-return.md) + +แต่อย่างไรก็ตาม ถ้าเราใช้ as หรือ satisfies เป็นการใช้ บังคับเปลี่ยน type ของ any โดยตั้งใจ TypeScript ถือว่า เรารับทราบปัญหาที่จะเกิดขึ้นในอนาคต + +หมายเหตุ: as หรือ satisfies มีประโยชน์ในหลายๆ เหตุการณ์ เลือกใช้ตามความเหมาะสม + +## ตัวอย่างการใช้ Fetch ที่อาจจะนำมาซึ่งปัญหา + +```ts +interface Person { + data: { + name: string; + }; +} +const url = "https://jsonplaceholder.typicode.com/todos/1"; +async function getUserData1(): Promise { + const result = (await fetch(url)).json(); + // ^? Promise + return result; +} + +async function getUserData2(){ + const result = (await fetch(url)).json(); + // ^? Promise + return result as Promise; // Same as `satisfies Promise` +} + +/** + * ไม่ได้ Error, ใน TypeScript, แต่ ❌ Error ที่ Runtime, TypeError: Cannot read properties of undefined (reading 'name') + **/ +console.log((await getUserData1()).data.name); +console.log((await getUserData2()).data.name); + +export {}; +``` + +ลองเล่น [Playground](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgArQM4HsTIN4BQyyAJnGHAFz5HHIhwC2E1GYUoA5gNy0C+vPgQQ42yAK5QANsgC8yAEQALMGAAOGSgHotAK2wg1UxBCVYpJaADowATzXARlqyMZawWElgxaAjAt44DFsQBGQYcVCwYBxkTggwAFUMaAARcjhfAAoASmpUKCxGYBSAHnQoAwA+GmIREDEoCAxxKTA5ZCy4AHc4YHaYBIQlLMkpHJyrfRxc3mIdOjoAPQB+NELisrgQWyraJrBJXCaWtsECAiCQsIiomNx4pJSodIoAJlza5HrG5tb2+RdXr9cJDEZjCZTAyzWgLRbIVbrIolCClba7fYJI7IE7-ZBBJGbVEVarceZaZAAZSYKAJAAMMOQSjBgM1CSjypgcFU6QQhAQtAAqQVEQXIQAicIBCOEAEnDiwAocIBJOGQAFEoIUoAAaZCAYThAJhwyAAKvYIJSEBw1GAtYBBOEAqHDS5CAGXIVWqsFBkIB0OEArHD2gBKkWizC1RrUEFV6uoAGFtiAsO0mnASMg1IUQ1BomysDAJCBLCyQBBE1l4yQuMgAOQMZhlnKiwVaS7BULhSIIaKxRh9ECfQh1UTmCBWKRYThZIF9dqPZJpDLZSFkChWSsQHJzb59qQDocjscgyfPV5wD5zjKLmkrvkXDugWZAA) + +# Const Assertions + +เราสามารถแปลง Object เป็น Readonly ผ่าน `as const` + +const assertions in Typescript +https://mainawycliffe.dev/blog/const-assertion-in-typescript/ + +```typescript +const person = { + name: "John Doe", + age: 25, +}; + +const person = { + name: "John Doe", + age: 25, +} as const; + +// (Note: This is not TypeScript Syntax) +// const person: { +// readonly name: "John Doe"; +// readonly age: 25; +// } +``` \ No newline at end of file diff --git a/paths/typescript/everyday-types/type-is-data.md b/paths/typescript/everyday-types/type-is-data.md new file mode 100644 index 0000000..4b2b82b --- /dev/null +++ b/paths/typescript/everyday-types/type-is-data.md @@ -0,0 +1,67 @@ +--- +outline: deep +--- + +# Type คือข้อมูลชนิดหนึ่ง + +## ประเภทของ Type + +1. Primitive types + + ```ts + type MyNumber = number; + type MyString = string; + type MyBoolean = boolean; + type MySymbol = symbol; + type MyBigInt = bigint; + type MyUndefined = undefined; + type MyNull = null; + ``` + +2. Literal types + + Literal types คือ Type ที่มีค่านั้น เพียงค่าเดียว + + ```ts + type MyNumberLiteral = 20; + type MyStringLiteral = 'Hello'; + type MyBooleanLiteral = true; + ``` + +3. Data structures (Object Types) + + ```ts + type MyObject = { + key1: boolean; + key2: number; + }; + type MyRecord = { + [key: string]: number; + }; + type MyTuple = [boolean, number]; + type MyArray = number[]; + ``` + +4. Unions + + ```ts + type MyUnion = 'Yes' | 'No'; + // Example + const myUnion1: MyUnion = 'Yes'; + const myUnion2: MyUnion = 'No'; + const myUnion3: MyUnion = 'Maybe'; // Error + ``` + +5. Intersections + + ```ts + type MyIntersection = { count: number } & { title: string }; + // Example + const myIntersection1: MyIntersection = { + count: 1, + title: 'Hello', + }; + ``` + + +อ้างอิง: https://type-level-typescript.com/types-are-just-data \ No newline at end of file diff --git a/paths/typescript/handbook/conditional-types.md b/paths/typescript/handbook/conditional-types.md new file mode 100644 index 0000000..3b2b3bf --- /dev/null +++ b/paths/typescript/handbook/conditional-types.md @@ -0,0 +1,27 @@ +# conditional types (แบบมีเงื่อนไข) + +อธิบาย keyword สำคัญๆ + +- `extends` เราสามารถคิดได้ว่าเป็นเงื่อนไขใน javacsript ก็ได้ คล้ายๆ กับการใช้ `===` เปรียบเทียบค่า +- `type` คิดว่าเหมือนเป็น function แต่เป็นสำหรับ type มันคือการใส่ input (ซึ่งก็คือ type เราเรียกว่า generics) แล้วก็มี output โดยที่ output ก็ขึ้นอยู่กับ "เงื่อนไข" ของ type นั้นๆ `extends` ก็เหมือนกับ block ของ logic เหมือนกับ if +- `infer` เปรียบเหมือนแว่นขยายใน TypeScript, ทำหน้าที่ในการ extract type ออกมาจาก type อื่นๆ + +## การ Extract property type ออกจาก object + +```typescript +type ObjectInfer = + // ถ้า Object T มี property ที่ชื่อ name อยู่ + T extends {name: infer A} + // ถ้ามี ให้ return type A (type ของ property name) + ? A + // ถ้าไม่มี ใ้ห return never + : never; + +const member = { name: 'John' }; + +type nameType1 = ObjectInfer; // จะได้ type เป็น string +type nameType2 = ObjectInfer; // จะได้ type เป็น never +``` + +## อ่านเพิ่มเด้อ +- [Learn Advanced TypeScript Types](https://medium.com/free-code-camp/typescript-curry-ramda-types-f747e99744ab) (3.7k claps) \ No newline at end of file diff --git a/paths/typescript/handbook/enum.md b/paths/typescript/handbook/enum.md new file mode 100644 index 0000000..35c23bf --- /dev/null +++ b/paths/typescript/handbook/enum.md @@ -0,0 +1,93 @@ +# Enum + +ถ้าเราใช้งาน enum เราไม่สามารถ assign string ตรงๆ ไปได้ เช่น เราไม่สามารถใส่ `DRAFT` ลงไปใน `PostState` ได้ + +```typescript +enum PostState { + Draft = "DRAFT", + Scheduled = "SCHEDULED", + Published = "PUBLISHED", +} + +const state1: PostState = PostState.Draft; +const state2: PostState = "IDEA"; // ❌ Error: Type '"IDEA"' is not assignable to type 'PostState' +const state3: PostState = "DRAFT"; // ❌ Error: Type '"DRAFT"' is not assignable to type 'PostState'. +``` + +บางทีเราสามารถใช้ `as` เพื่อ casting type ได้ แต่มันก็ไม่เกิดประโยชน์เท่าไหร่ เพราะความสามารถในการเช็ค Type หายไป + +```typescript +const state2: PostState = "IDEA" as PostState.Draft; // ตรงนี้มันควรจะ Error ใช่มั้ย +const state3: PostState = "DRAFT" as PostState.Draft; +``` + +ปัญหาอีกอย่าง ถ้าเราใช้ `enum` อยู่ใน Library แล้วเราไม่สามารถส่ง string `"DRAFT"` ที่มีใน `enum PostState` ได้เลย +เราอาจจะต้อง import enum เข้ามาใช้งาน และถ้าเป็นภาษา JS ละ ?? + +## เราสามารถใช้ const object แทน enum ใน TypeScript ได้ + +```typescript +const PostState = { + Draft : "DRAFT", + Scheduled : "SCHEDULED", + Published : "PUBLISHED", +} as const; +``` + +เมื่อเราใช้ `as const` แต่ละ property ของ object จะเป็น `readonly` ทันที + +![Enum Object Const](./images/enum-object-const.png) + +เราสามารถทำ Type Alias เพื่อมา check type ของ `PostState` ได้ + +```typescript +type PostStateType = PostState; // ❌ Error เราไม่สามารถเอา js variable มาเป็น type ได้ +type PostStateType = typeof PostState; //เราสามารถใช้ `typeof` เพื่อเอา type ของ js variable มาได้แทน +``` + +แบบนี้เราจะได้ Key ของ object `PostState` + +```typescript +type PostStateType = keyof typeof PostState; +// type PostStateType = "Draft" | "Scheduled" | "Published" +``` + +แบบนี้เราจะได้ Value ของ object `PostState` พอเป็นแบบนี้เราก็เอามา check type ได้แล้ว + +```typescript +type PostStateType = typeof PostState[keyof typeof PostState]; +// type PostStateType = "DRAFT" | "SCHEDULED" | "PUBLISHED" +``` + +## เรามาดูตัวอย่างแบบเต็มๆ กันดีกว่า + +```typescript +const PostState = { + Draft : "DRAFT", + Scheduled : "SCHEDULED", + Published : "PUBLISHED", +} as const; + +type PostStateType = typeof PostState[keyof typeof PostState]; +// type PostStateType = "DRAFT" | "SCHEDULED" | "PUBLISHED" + +const state1: PostStateType = PostState.Draft; // อันนี้ เราสามารใช้งานได้เหมือน enum เลย +const state2: PostStateType = "IDEA"; // อันนี้ ❌ Error: Type '"IDEA"' is not assignable to type 'PostStateType' +const state3: PostStateType = "DRAFT"; // เย้ๆ เราสามารถ passing string ที่ match กับ value ใน PostState ได้แล้ว +``` + +ขอบคุณตัวอย่างจากคุณ Andrew Burgess [How to use TypeScript Enums and why not to, maybe](https://www.youtube.com/watch?v=pWPClHdcvVg) + +## ทำ Type Utility ใช้งานเอง + +```typescript +// Type Utility +type Enum = T[keyof T]; + +type PostStateType = Enum; +``` + + +## อ่านเพิ่มน้าาา +- https://shaky.sh/ts-bit-flags/ +- [Number Enums as flags](https://basarat.gitbook.io/typescript/type-system/enums#number-enums-as-flags) \ No newline at end of file diff --git a/paths/typescript/handbook/function-overload.md b/paths/typescript/handbook/function-overload.md new file mode 100644 index 0000000..bda6f79 --- /dev/null +++ b/paths/typescript/handbook/function-overload.md @@ -0,0 +1,3 @@ +# Function Overloading + +https://dmitripavlutin.com/typescript-function-overloading/ \ No newline at end of file diff --git a/paths/typescript/handbook/generics.md b/paths/typescript/handbook/generics.md new file mode 100644 index 0000000..28b04e0 --- /dev/null +++ b/paths/typescript/handbook/generics.md @@ -0,0 +1,4 @@ + +# Generics + + \ No newline at end of file diff --git a/paths/typescript/handbook/images/enum-object-const.png b/paths/typescript/handbook/images/enum-object-const.png new file mode 100644 index 0000000..67901a3 Binary files /dev/null and b/paths/typescript/handbook/images/enum-object-const.png differ diff --git a/paths/typescript/handbook/keyof.md b/paths/typescript/handbook/keyof.md new file mode 100644 index 0000000..51f942d --- /dev/null +++ b/paths/typescript/handbook/keyof.md @@ -0,0 +1,51 @@ +# Keyof + +ในบทความนี้ ผมจะอธิบายละเอียดขั้นตอนในการสร้างฟังก์ชันที่สามารถดึงค่าจากคีย์ของ Object ใน TypeScript + +สมมติ เริ่มต้นด้วยการกำหนดโครงสร้างของ Object ใน Post ดังนี้: + +```ts +type Post = { + title: string; + date: Date; +}; + +const myPost: Post = { + title: "TypeScript is great!", + date: new Date(), +}; +``` +1. สมมติว่า เราต้องการสร้าง Function ที่ดึง Value ออกมาจาก Key ของ Object `myPost` นี้ + + ```ts + getPostByKey1("title", myPost); // TypeScript is great! + ``` +2. ถ้าเราพยายามใช้ key ในรูปแบบ string สำหรับฟังก์ชัน getPostByKey2 เราจะเจอข้อผิดพลาด เนื่องจากคีย์ต้องเป็น title หรือ date + ```ts + function getPostByKey2(key: string, postData: Post) { + return postData[key]; + // Error: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Post'. + } + ``` +3. วิธีแก้ไขคือการกำหนดคีย์ให้เป็น union type แสดงว่า key ต้องเป็น union type ที่มีค่าเป็น `'title' | 'date'` + ```ts + function getPostByKey3(key: 'title' | 'date', postData: Post) { + return postData[key]; + } + ``` +4. ใช้คำสั่ง keyof เพื่อได้ union ของคีย์ของ Object ทำให้เราสามารถกำหนดคีย์ที่ถูกต้องและใช้งานได้ + ```ts + type KeyOfPost = keyof Post; // 'title' | 'date' + ``` + +5. เราสามารถสร้างฟังก์ชันที่เป็น generic และนำไปใช้กับข้อมูลใด ๆ ได้ ด้วยฟังก์ชัน `getObjectByKey` ซึ่งใช้ keyof ของตัวแปร T ตัวอย่างนี้ใช้ `'date'` จาก object `myPost` + ```ts + function getObjectByKey(key: keyof T, data: T) { + return data[key]; + } + getObjectByKey('date', myPost); + ``` + +จากตัวอย่าง เราสามารถสร้างฟังก์ชันที่สามารถดึงค่าจากคีย์ของ Object อย่างมีประสิทธิภาพและยืดหยุ่นใน TypeScript ได้ ฟังก์ชัน `getObjectByKey` ที่เป็น generic สามารถนำไปใช้กับ Object หรือโครงสร้างข้อมูลชนิดอื่น ๆ ได้ตามความต้องการของคุณ + +ลองเล่นใน [Playground](https://www.typescriptlang.org/play?#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXwHMQMAFHAZwwCEBPAaRBoEYAKAa0YC5403UcA7qgA08AA4UM3MpQCU3XvyEBuAFCqMNMQhkZ4AXngBvVfHjYMEEN0owsqAmrPAoGa-AAirkGoC+a1TA8SngAWxpdaUkDY1NzLEt3ACIAFS0QAGUwOzE9LHJCOFcAQiThOJc3blQQAU9vFlly-1UAegAqdtN2+CYAOnhAVDhASThAWjhAcDhAQDhAJjhAYjhAKjhZ4enx+AAxNExcfEB0OEBWOEAJOEAUOEA2OFWANSgIZARR0cnAQjhpwAg4acn4Bhp4QCA4CfgAeQARgArcAYbqtVREUiSWifVhJCxWMphCKSWTKeCtVrwNLaLI5PIFAhFDDFNqdbrwABMA0AAnCzaaAETgHgd5tMnrNAGhwgGE4QBQcMN4BwvnTANhwgHE4QCYcPBbPYCPAmUdBXTAHhwjMADHAyjB2BwK1n8g7xRLwQDUcLNAOxwo3glRAENUKHQ2DwhGIujhjGp7C4Wp1BFEEkoXgwUCicliZmx8AAAhhyABaEAAD20mATMBgOBgcTgGGQMHwAYwQagAG1hQBdPwUrrwHoAZgGgEE4eZHcaAcjgDtMhYwhmNxmKpTxUNtzOkDAA+eAAciRICn8AAPtObVO7Q6ts7oW76Iw616aNxEQlkYv4EkbSjC8XQxhZOH4Dm8wXJMWy4xK6pfNWqQAWAbCnBEHgZ51UVQU0BHX5VmFH4-iBUFMDtTRtA+Rh-kQXQYgAoDdExSMZ2POdTynFd1A6GsegAVgGHZAGY4HkTUFdcnXwIgajsMB4CeSZvhOYZpXZTkuQfEBkHIKBASsBUlTXTYWJdDB4LBd0aAAHhScd924bDcVESoQ1xO8TDMR982tVxSwrKtoSUzAVJYEjvCnURwl0DEgA) \ No newline at end of file diff --git a/paths/typescript/handbook/mapped-types.md b/paths/typescript/handbook/mapped-types.md new file mode 100644 index 0000000..46ce0cd --- /dev/null +++ b/paths/typescript/handbook/mapped-types.md @@ -0,0 +1,4 @@ +# Mapped Types + +Ref: https://www.typescriptlang.org/docs/handbook/2/mapped-types.html + diff --git a/paths/typescript/handbook/narrowing-type.md b/paths/typescript/handbook/narrowing-type.md new file mode 100644 index 0000000..91f42af --- /dev/null +++ b/paths/typescript/handbook/narrowing-type.md @@ -0,0 +1,54 @@ +# Narrowing Type + +คือการตัด case ความเป็นไปได้ของ Type ที่เกิดขึ้น เพื่อให้เราจัดการกับ ชนิดของ Object ได้ถูกต้องนั้นเอง + +- https://www.codecademy.com/learn/learn-typescript/modules/learn-typescript-type-narrowing/cheatsheet +- https://formidable.com/blog/2022/narrowing-types/ + +## การ narrow type ของ unknown + +```ts +/** + * A custom type guard function that determines whether + * `value` is an array that only contains numbers. + */`` +function isNumberArray(value: unknown): value is number[] { + return ( + Array.isArray(value) && value.every(element => typeof element === "number") + ); +} + +const unknownValue: unknown = [15, 23, 8, 4, 42, 16]; + +if (isNumberArray(unknownValue)) { + // Within this branch, `unknownValue` has type `number[]`, + // so we can spread the numbers as arguments to `Math.max` + const max = Math.max(...unknownValue); + console.log(max); +} +``` + +Ref: +- https://mariusschulz.com/blog/the-unknown-type-in-typescript +- https://mainawycliffe.dev/blog/type-guards-and-narrowing-in-typescript/ +- https://bobbyhadz.com/blog/typescript-type-unknown-is-not-assignable-to-type + +ทีนี้มาลอง Narrow เพื่อเช็ค Record Type กัน โดยใช้ lib data validation ที่ชื่อ [zod](https://github.com/colinhacks/zod) + +```ts +import { z } from 'zod'; + +function isRecord(object: unknown): object is Record { + const recordSchema = z.record(z.unknown()); + return recordSchema.safeParse(object).success; +} + +function findLength(object: unknown): number { + if (Array.isArray(object)) { + return console.log(object.length); + } else if (isRecord(object)) { + return Object.keys(object).length; + } + throw new Error(`Input object doesn't support type`); +} +``` \ No newline at end of file diff --git a/paths/typescript/handbook/type-manipulation.md b/paths/typescript/handbook/type-manipulation.md new file mode 100644 index 0000000..a519e81 --- /dev/null +++ b/paths/typescript/handbook/type-manipulation.md @@ -0,0 +1,19 @@ +# การปรับแต่ง Type + +## keyof + +```ts +type Person = { + name: string; + age: number; +} + +type keyOfPerson = keyof Person; +// Similar to type keyOfPerson = 'name' | 'age': +``` + +```ts +type keyOfAny = keyof any; +// Similar to type keyOfAny = string | number | symbol +``` + diff --git a/paths/typescript/handbook/utility-types.md b/paths/typescript/handbook/utility-types.md new file mode 100644 index 0000000..634dbd5 --- /dev/null +++ b/paths/typescript/handbook/utility-types.md @@ -0,0 +1,148 @@ +# Utility Types + +https://www.typescriptlang.org/docs/handbook/utility-types.html + + +## Awaited + +ใช้ `Awaited` สำหรับการถอด Promise Type ออกมา และสามารถถอดแบบ Recursive ลึกจนถึง Value ได้ + +```typescript +type A = Awaited>; +// type A = string + +type B = Awaited>>; +// type B = number + +type C = Awaited>; +// type C = boolean | number +``` + +## Partial + +ทำให้ทุก Property ของ Type เป็น optional value; + +```typescript +interface Todo { + title: string; + description: string; +} + +type PartialTodo = Partial; + +// type PartialTodo = { +// title?: string | undefined; +// description?: string | undefined; +// } +``` + +## Required + +ทำให้ทุก Property ของ Type เป็น required value + +```typescript +interface Todo { + title?: string; + description?: string; +} + +type RequiredTodo = Required; + +// type RequiredTodo = { +// title: string; +// description: string; +// } +``` + +## Readonly + +ทำให้ทุก Property ของ Type เป็น readonly (const) ไม่สามารถแก้ไขค่าได้ + +```typescript +interface Todo { + title: string; +} + +type ReadonlyTodo = Readonly; + +// type ReadonlyTodo = { +// readonly title: string; +// } + +// Example + +const todo: ReadonlyTodo = { + title: "Delete inactive users", +}; + +todo.title = "Hello"; // ❌ Error: Cannot assign to 'title' because it is a read-only property +``` + +เราสามารถใช้ `as const` เพื่อทำให้เป็น readonly ได้เหมือนกัน (Only Type Assertion) [อ่านเพิ่มที่](../everyday-types/type-assertions) + +ถ้าเราอยากให้ Error ที่ Run time แนะนำให้ใช้ `Object.freeze(obj)` เช่น + +```typescript +function freeze(obj: Type): Readonly { + return Object.freeze(obj); +} + +const todo = { + title: 'Running' +}; + +const freezedTodo = freeze(todo); + +freezedTodo.title = 'Lazy'; // ❌ Type Error and Error at run time +``` + +## Record + +```typescript +const nameRecord: Record = { + john: 1, + micky: 2, +}; + +nameRecord['Lilly'] = 5; // We can add new property in object becuase 'Lilly' is string + +nameRecord[true] = 5; // ❌ Error: Type 'true' cannot be used as an index type +``` + +## Pick + +เลือกบาง Property ออกมาจาก Object + +```typescript +interface Todo { + title: string; + description: string; + completed: boolean; +} + +type PickTodo = Pick; + +// type PickTodo = { +// title: string; +// completed: boolean; +// } +``` + +## Omit + +ลบบาง Property จาก Object + +```typescript +interface Todo { + title: string; + description: string; + completed: boolean; +} + +type OmitTodo = Omit; + +// type OmitTodo = { +// title: string; +// completed: boolean; +// } +``` diff --git a/paths/typescript/index.md b/paths/typescript/index.md new file mode 100644 index 0000000..6ad0224 --- /dev/null +++ b/paths/typescript/index.md @@ -0,0 +1,15 @@ +--- +outline: deep +title: 'TypeScript Roadmap' +description: ขั้นตอนแนะนำในการเตรียมความพร้อมในการเข้าสู่เส้นทาง TypeScript ในปี 2024 +--- + +# TypeScript + +TypeScript เป็นภาษาโปรแกรมที่ถูกพัฒนาโดย Microsoft และเป็นภาษาที่สร้างขึ้นบน JavaScript โดยมีการเพิ่มเติมความสามารถในการเขียนโปรแกรมให้มีความเป็นระเบียบมากขึ้น และเพิ่มความเป็นมืออาชีพในการพัฒนาโปรแกรม + +เนื้อหาส่วนหนึ่งจากหนังสือ [คู่มือ TypeScript สำหรับคนไทย](https://typescript-th.thadaw.com/) +และ [Official TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/intro.html) + +สำหรับใครอยากเรียนรู้ TypeScript ขั้นสูง เพื่อสำหรับออกแบบ Type-safe Design Pattern ในหนังสือ [Type-safe Design Pattern +in Modern TypeScript](https://type-safe.thadaw.com/) \ No newline at end of file diff --git a/paths/web-guideline/database/mongodb.md b/paths/web-guideline/database/mongodb.md index 00ffeec..dbeaed3 100644 --- a/paths/web-guideline/database/mongodb.md +++ b/paths/web-guideline/database/mongodb.md @@ -3,11 +3,14 @@ **MongoDB** คือ เป็นระบบฐานข้อมูล NoSQL ประเภท Document Model ที่มีจุดเด่นที่เรียกว่า Flexible Schemas คือ ไม่จำเป็นต้องมี การกำหนดโครงสร้างข้อมูลชัดเจน ตั้งแต่แรกก็ได้ หรือจะมีก็ได้ ตามความต้องการของผู้ใช้งาน โดยมีลิขสิทธิ์แบบ **ใช้งานได้ฟรี (Community Editon) และ เสียค่าลิขสิทธิ์ (Enterprise Advanced) โดยมีทั้งแบบ on-premise และ on-cloud (MongoDB Atlas) ได้รับความนิยมในปัจจุบัน** **Certifications** หรือ ใบรับรอง สำหรับผู้ที่ต้องการพิสูจน์ความสามารถในสาย MongoDB เพื่อเป็นที่ยอมรับในระดับสากลมากยิ่งขึ้น ปัจจุบัน MongoDB มีใบรับรอง ความสามารถทั้งหมดดังนี้ -- MongoDB Associate Developer -- MongoDB Associate Database Administrator -- MongoDB Associate Data Modeler -- MongoDB Assoicate Atlas Administrator +- MongoDB Associate Developer - เหมาะสำหรับผู้ที่สนใจอยากวัดความรู้ทางด้าน Developer +- MongoDB Associate Database Administrator - เหมาะสำหรับผู้ที่สนใจอยากวัดความรู้ทางด้าน DBA +- MongoDB Associate Data Modeler - เหมาะสำหรับผู้ที่สนใจอยากวัดความรู้ทางด้าน การออกบบโครงสร้างจัดเก็บของมูลตาม รูปแบบของ MongoDB +- MongoDB Assoicate Atlas Administrator - เหมาะสำหรับผู้ที่สนใจอยากวัดความรู้ทางด้าน การเป็นผู้ดูแลจัดการ MongoDB Atlas เว็บไซด์แหล่งเรียนรู้สำหรับคนที่ต้องการศึกษา MongoDB ทั้งที่เป็นภาษาไทย และภาษาอังกฤษ มีดังนี้ -[https://learn.mongodb.com/](https://learn.mongodb.com/) - เป็น Learning Platform Official ของทาง MongoDB ซึ่งจะสอนตั้งแต่ขั้นพื้นฐานไปจนถึงระดับผู้เชี่ยวชาญครับ \ No newline at end of file +- [MongoDB Learning Platform](https://learn.mongodb.com/) - (ภาษาอังกฤษ) คือ Learning Platform Official ของทาง MongoDB ซึ่งจะสอนตั้งแต่ขั้นพื้นฐานไปจนถึงระดับผู้เชี่ยวชาญครับ +- [MongoDB Developer Center](https://www.mongodb.com/developer/) - (ภาษาอังกฤษ) คือ MongoDB Developer Center Official ของทาง MongoDB ที่จะมี article และ ตัวอย่างที่ใช้งานได้จริง สอนการนำ MongoDB ไปประยุกต์ใช้กับงานด้านต่างๆ +- [MongoDB Developer Community](https://www.mongodb.com/community/forums/) - (ภาษาอังกฤษ) คือ Official forums สำหรับถามตอบปัญหาเกี่ยวกับการใช้งาน MongoDB ในด้านต่างๆ จากทั่วโลก +- [MongoDB Thailand User Group](https://www.facebook.com/groups/104161329726941) - (ภาษาไทย) คือ FB group สำหรับ กลุ่มคนใช้งาน MongoDB ในประเทศไทย รวมทั้งมีจัดงาน meetup ทั้งแบบ offiline และ online อยู่อย่างต่อเนื่่อง