Static Type คืออะไร
Tags: computerscience, engineering, typescript, beginners
Published: 2020-08-04 03:53:32

กลุ่มเป้าหมายของบทความนี้คืออธิบายเครื่องมือทางเลือกสำหรับ Programmer, developer, software engineer มือใหม่ที่เริ่มต้นจากภาษายอดนิยมอย่าง เช่น Python และ JavaScript


___


เคยเจ็บปวดรวดร้าวกับการที่ code base เริ่มขยายใหญ่ขึ้นเรื่อยๆ แล้วไม่รู้ว่า function รับตัวแปรเป็น type อะไร แล้วคืนค่าเป็น type อะไร หรือเป็น object หน้าตายังไง มี method อะไรให้เรียกใช้ จนต้องไปไล่หา code ทั้งโปรเจกต์ เพียงเพื่อจะต่อเติมเสริมสร้าง feature เล็กน้อยอันเดียวให้ถูกต้องกันรึเปล่า? แล้วเสียเวลากันไปเท่าไหร่? กู้หนี้ technical debt จนใกล้ล้มละลายกันมาบ้างมั้ย

จะบรรเทาความเจ็บปวดเหล่านี้ยังไง? ทำ test ให้หมดไม่ได้เหรอ ก็อาจจะได้อยู่ แต่มันก็เยอะขึ้นตาม code base อยู่ดี และเขียน test ก็ต้องระวังเรื่อง type เองอยู่ดี...

---

# Dynamic Type

ปัญหาเหล่านี้มักเกิดขึ้นกับภาษาที่เป็น dynamic type ซึ่งเป็นภาษาที่ **ตรวจสอบ type ช่วง run-time **  และมักจะไม่สามารถกำหนด type ของตัวแปรและค่าที่ได้จาก function โดยผู้เขียนเอง โดยถ้ามีการทำงานที่ผิดพลาดจากการทำงานผิดหลักของ type ก็มักจะพังตอนรันไปแล้ว
และภาษาประเภทนี้มักเป็นที่นิยมของ developer, programmer หรือผู้ที่หัดเขียนโปรแกรมในปัจจุบัน เช่นภาษา Python และ JavaScript 

ในเมื่อมีปัญหาที่ว่ามา แล้วทำไมยังใช้ภาษาประเภทนี้กันอยู่? ก็มันไม่ได้เกิดปัญหานี้กับ code base ทุกที่ครับ  ที่จริงแล้วภาษาแบบ dynamic type มีข้อดีมากมาย(และทำให้เป็นที่นิยม) เช่น
1. เรียนรู้ได้เร็ว
2. code ค่อนข้างกระชับ
3. จัดการข้อมูลที่มีโครงสร้างซับซ้อนและลื่นไหลได้ง่าย 
4. ใช้เวลาเขียนค่อนข้างเร็วก็สามารถเข็น product ที่ใช้ได้ 

และถ้า code base ไม่ได้ใหญ่มาก ก็ใช้การ test ทดสอบความถูกต้องได้มากอยู่

---

## แล้วอธิบายภาษา dynamic type มาตั้งนานยังไม่อธิบายเลยว่า static type ในชื่อบทความคืออะไรเลย?

ภาษา static type คือภาษาที่มี **การตรวจสอบ type ตอน compile-time ** นั่นเองครับ (ฮา) โดยภาษาประเภทนี้ หาก code มีการทำงานใดๆ ผิดหลัก ตัวโปรแกรมเองก็จะไม่สามารถรันได้เลย เช่น นำ string ไปคูณกับ integer, เรียกใช้ method ที่ไม่มีจริงของ object, เรียกใช้ function โดยใส่ argument ไม่ครบ เป็นต้น


*** ทว่า แต่ละภาษาก็จะมี type ของข้อมูล และข้อกำหนดหรือข้อจำกัดรวมไปถึง feature ที่ต่างกันไป  โดยตัวอย่างภาษา static type ที่ผมสาธิตจะเป็นภาษา TypeScript version 3.9.7 ครับ ***

### ตัวอย่างการกำหนด type ให้ตัวแปร, function ของภาษา TypeScript

``` TypeScript
const myNumber: number = 1; 
// กำหนดประเภทของตัวแปรหลัง colon โดยในที่นี้ myNumber จะมี type เป็น number

const myString: string = 'some string'; 
// เป็น string

const wrongType: number = 'some number'; 
// เกิด error ไม่สามารถ assign ข้อมูล string ให้ตัวแปรที่เป็น number ได้

const inferedNumber = 1; 
/* ไม่ได้กำหนด type ของตัวแปร 
แต่ compiler มีการ infer (อนุมาน) type ของตัวแปร
จากข้อมูลที่ assign ให้ โดยในที่นี้จะเป็น number
*/

const inferedString = 'infered string' 
// เช่นกัน inferedString จะมี type เป็น string


function multiply(x: number,y: number): number {
    return x*y;

} 
/* กำหนด type ให้ parameters ได้ 
และกำหนด type ของค่าที่ได้จาก function ได้ด้วย 
ในที่นี้ function multiply จะคืนข้อมูลเป็น number
*/
const arrowFunctionMultiply = (x: number,y: number): number => {
    return x*y;
}
 // arrow function ก็กำหนดได้เช่นกัน

const inferedFunctionReturnType = (x:number, y:number) => {
    return x*y;
} 
// มีการ infer type ของค่าที่ return จากฟังก์ชันได้เช่นกัน
```

### ตัวอย่างการใช้ operation กับ type ที่ไม่รองรับ ระหว่าง JavaScript กับ TypeScript

JavaScript
```JavaScript
const x = 1;
const y = 'A';
console.log(x);// 1
console.log(y);// "A"
console.log(x * y);// NaN

```

TypeScript
```TypeScript
const x = 1;
const y = 'A';
console.log(x);
console.log(y);
console.log(x * y); 
/* เกิด compile error -> The right-hand side of an arithmetic operation 
must be of type 'any', 'number', 'bigint' or an enum type 
และไม่สามารถ compile เพื่อรันได้
// ซึ่งก็คือ TypeScript ไม่อนุญาตให้ใช้ operator * ระหว่าง number กับ string 

```
### ตัวอย่างการตรวจสอบการเรียก function ระหว่าง JavaScript กับ TypeScript

JavaScript

```JavaScript
function multiply(a, b, c) {
  console.log(a,b,c);
  return a * b * c;
}

multiply(1); //  1 undefined undefined และได้ค่า NaN

multiply(1,'2','3') // 1 "2" "3" และได้ค่า NaN


```

TypeScript
```TypeScript

function multiply(a:number, b:number, c:number) {
  console.log(a,b,c);
  return a * b * c;
}
multiply(1);
// จะเกิด compile error -> Expected 3 arguments, but got 1. รันไม่ได้
multiply(1,'2','3') 
/*  compile error -> Argument of type '"2"' 
is not assignable to parameter of type 'number'
*/
```
### ตัวอย่างการเรียก method ที่ไม่มีอยู่จริง ระหว่าง JavaScript กับ TypeScript

JavaScript
``` JavaScript
const exampleString = 'This is a string.';
console.log(exampleString); // "This is a string."
const invalidOperation = exampleString.map(str => str + '1');
 
// โปรแกรมทำงาน แต่เกิด TypeError: exampleString.map is not a function
```

TypeScript
```TypeScript
const exampleString = 'This is a string.';
console.log(exampleString); 
const invalidOperation = exampleString.map(str => str + '1');
 
// เกิด compile error Property 'map' does not exist on type string
```

### ตัวอย่างของ "Gotcha" ในภาษา TypeScript

```TypeScript

const exampleNumber = 12; // ได้ number
const exampleString = "12"; // ได้ string
const weirdGotcha = exampleNumber + exampleString; // จะได้string "1212" 

/*
นี่เป็นตัวอย่างข้อยกเว้น ข้อควรระวัง ภาษา TypeScript เป็นภาษาที่ต่อยอดจาก JavaScript 
ซึ่ง number + string จะแปลง number เป็น string และนำมาต่อกัน  
โดยภาษา TypeScript ยังคงอนุญาตให้ใช้ number + string ได้
*/
```
---
# ปิดท้าย
จะเห็นได้ว่า ความผิดพลาดพื้นๆ ที่อาจเกิดขึ้นได้จากการที่ code base ใหญ่ขึ้นเช่นการเรียก method ผิด, เรียกใช้ function จาก module ผิดวิธี จะถูกจับไว้ก่อนจะได้รันโปรแกรมเสียอีก และถ้าใช้ code editor ที่รองรับการแสดงผล compiler, document ก็ยิ่งทำให้อ่านการใช้งานระหว่างการเขียนโค้ดได้อย่างทันท่วงที ชี้ไปที่ argument ของ function ก็เห็นเลยว่ามี method อะไรบ้าง    พอกันทีกับการที่โปรแกรมเราไปตายใน production เพียงเพราะเรียก method ผิด! พอกันทีกับการที่ต้องมานั่งดูโค้ดทั้งโปรเจ็กต์เพราะจำไม่ได้ว่า function ที่เขียนเดือนก่อนมันคืนค่าอะไรกลับมา!

เพราะฉะนั้นแล้ว หันมาใช้ภาษา static ty-
![เดี๋ยวก่อน](https://static1.srcdn.com/wordpress/wp-content/uploads/2020/05/Batman-Slapping-Robin-Meme-Explained.jpg?q=50&fit=crop&w=960&h=500&dpr=1.5 "ใจเย็นว้อย")

---
# ใจเย็น อย่าเพิ่ง Hype แล้วเอาไปใช้กับทุกอย่าง


ภาษาประเภท static type ก็มีข้อเสียเช่นกัน เช่น
1. มักจะมีการ compile ทำให้เพิ่มเวลาในการ build app เพิ่ม
2. ใช้เวลาเรียนรู้มากกว่า dynamic type 
3. จัดการข้อมูลที่มีโครงสร้างซับซ้อนและลื่นไหลได้ยากกว่า dynamic type (JSON แค่กๆ)
4. code อาจจะยาวเยอะโดยไม่จำเป็น (verbose) ทำให้อ่านได้ช้า
5. ต้องใช้เวลาในการเขียน code ในช่วงแรกนานกว่าแบบ dynamic type โดยจะเสียเวลาไปกับการตรวจสอบความถูกต้องก่อนจะรันได้ แลกกับการที่ต่อเติม code ได้ง่ายกว่าในระยะยาว จึงไม่ค่อยเหมาะกับโปรเจ็กต์ขนาดเล็กหรือ script เรียบง่ายที่ต้องการใช้เวลาพัฒนาสั้นๆ

---
กระนั้นแล้วข้อดีของภาษา static type นั้นก็ทำให้ code base มีการ maintain ที่ง่ายขึ้น ตัว code เองก็มีการตรวจสอบแล้วว่าต้องเรียกใช้งานอะไรยังไงบ้างโดยไม่ต้องพึ่งพา test  ลดปัญหาทางเทคนิคไปมากในระยะยาว แต่ก็ไม่ได้รับรองนะครับว่า code ที่เขียนไปมีพฤติกรรมทาง business ตามต้องการทั้งหมด

ผู้พัฒนาและดูแลภาษา dynamic type ก็เห็นประโยชน์ดังกล่าว และได้เริ่มเพิ่มการตั้ง type ในภาษา dynamic type กันมากขึ้น โดยมักจะอยู่ในรูป type hint ซึ่งก็คือการเขียนคอมเมนต์ตามฟอร์แม็ตที่เจาะจง(decorator) เพื่อระบุ type, parameter ของ function 
[JavaScript มี JSDoc](https://jsdoc.app/) 


[Python มี Typing](https://docs.python.org/3/library/typing.html)   

บางภาษาก็เป็นลูกผสมเช่น C++ เป็นต้น


---

# ปิดท้ายจริงแล้วครับ


หวังว่าผู้อ่านจะเข้าใจ dynamic type กับ static type มากขึ้น และได้รู้ข้อดีข้อเสียเพื่อนำไปใช้งานที่เหมาะมากขึ้น 

ที่ปูกันมายาวแบบนี้ ก็เพื่อจะได้เขียนเรื่อง Interface ไว้เจอกันนะครับ!

หมายเหตุ: ส่วนความ strongly typed กับ weakly typed นั้นไม่ได้มีความสัมพันธ์โดยตรงกับการเป็น static หรือ dynamic ครับ

หมายเหตุ 2: ภาษา TypeScript เป็นภาษาต่อขยายจาก JavaScript โดย code ของมันจะ compile ไปเป็นภาษา JavaScript แล้วจึงรันด้วย JavaScript engine เช่น Browser ทั้งหลาย (V8 ของ Chrome, SpiderMonkey ของ Firefox), NodeJS 

# แก้ไขครั้งที่ 1 

มี feedback เรื่องความเร็วของภาษา static type กับภาษา dynamic type 
ความเร็วที่ถูกกล่าวถึงในโพสต์นี้มีเพียง "ความเร็วในการพัฒนา" เท่านั้นครับ  แต่ตอนเขียนข้อดีข้อเสียเปรียบเทียบกันผมก็ละเลยเรื่อง "ความเร็วตอนรัน" ไป

## ความเร็วตอนรัน
ภาษา static type นั้น *ส่วนมาก* จะมีความเร็วตอนรันสูงกว่าภาษา dynamic type เพราะว่าภาษา dynamic type จะมีการตรวจสอบ type ตอนรัน ในขณะที่ภาษา static type จะไม่มีการตรวจสอบตอนรันแล้ว

## กลับมาที่ความเร็วในการพัฒนา

กราฟนี้อธิบายได้ดีมากครับ

![comparison](https://miro.medium.com/max/700/1*mGoThqiR2gd4HzNiltptMw.png "the graph")
Credit: miro.medium.com

กราฟนี้ แกน X คือขนาดของ code base  ส่วนแกน Y คือ productivity ครับ

ส่วนรายละเอียดของความเร็วในการพัฒนาที่แตกต่างกันระหว่างภาษาสองประเภทนี้ อาจจะยกไว้เป็นโพสต์อื่นนะครับ