สรุป Syntax ของ TypeScript หากจะย้ายจาก JavaScript ต้องรู้อะไรใหม่บ้าง

NSLog0
NSLog0
Mar 8, 2020 · 5 min read

บทความนี้จะมาสรุป Syntax เรื่องการเขียน TypeScript สำหรับคนที่เขียน JavaScript เป็นประจำอยู่แล้ว (ซึ่งผมกำลังจะย้ายมาเขียน TS) หากจะย้ายมาเขียน TypeScript หรือทำการ Migrate โปรเจคที่เคยทำไว้ให้เป็น TypeScript เราจะต้องแก้อะไรบ้าง ต้องบอกก่อนว่าผมไม่ได้มาสอนทั้งหมด เพียงแค่เบี้องต้นไว้สำหรับทำงานช่วงแรกๆ ก่อน สำหรับการใช้ฟีเจอร์ที่ลึกลงไป ถ้ามีโอกาสผมจะมาอธิบายให้ฟัง เพราะตอนนี้ผมก็เพิ่งหัดใช้เหมือนกัน แต่ก่อนอื่นเลยไปติดตั้ง TypeScript กันก่อนที่ link

Why TypeScript

  • เป็น Complie Time Error เราจะสามารถเห็น Error ได้ตั้งแต่ตอนเขียนว่าระบบเราจะบัคตรงไหนบ้าง
  • รองรับการเขียนแบบ Strong type หรือ Static type หรือพูดง่ายๆ ว่ามีต้องประกาศ type ของตัวแปร
  • ทำงานได้กับทุกๆ Browser และ JS Engine
  • ใช้เวลาเขียนนานหน่อยแต่ง่ายต่อการ debug และการหา Error มาก
  • เหมาะกับการใช้ JavaScript ในการทำระบบที่มีขนาดใหญ่ อย่างเช่น NodeJS เพราะสาเหตุของการเป็น Typesafe
  • JavaScript เป็น Runtime Error เพราะงั้นเราจะไม่เจอบัคจนกว่าจะรันไฟล์

เหตุผลคร่าวๆ คงจะประมาณนี้ผมคงจะไม่ลงลึกมาก เดี๋ยวเราจะมาเริ่มสรุป Syntax กันเลย

Variable Declaration

Primitive

// Number
const n1: number = 1
const n2: number = 1.1
// Boolean
const n3: boolean = true
// Array
const n5: string[] = ['1','2','3']
// String
const n8: string = 'It\'s a string'

Generic

รองรับการทำ Generic สำหรับสาย Java, C# น่าจะถูกใจอย่างมาก หากใครอยากศึกษามากกว่านี้แนะนำว่าลองอ่านในบทความนี้ครับ link

const n1: Array<String> = ['1','2','3']
const n2: Array<any> = [1, 'Apple',true, 1.2]

Any

ในกรณีที่เราอยากให้ตัวแปรเรารับ Type อะไรก็ได้ หรือพูดง่ายๆ ว่าทำให้เป็น Dynamic type เราก็จะประกาศเป็น any ให้มัน ก็จะเหมือนกันการเขียน JavaScript ทั่วไปที่ตัวแปร

let n1: any = 1
n1 = 1.2
n1 = true
n1 = [1,2]

Void

ถ้าใครเขียน Java หรือ C#, C, C++ น่าจะคุ้นเคยกันอย่างดี แต่ถ้าใครไม่เคยเลย มันคือ Type ที่ไว้บอกตัว Complier ว่าไม่มีการ return อะไรออกมาจากฟังก์ชัน เดี๋ยวตรงนี้จะอธิบายในหัวข้อการประกาศฟังก์ชัน หรือถ้าใส่ในตัวแปรก็จะได้ค่า undefined หรือ null

Tuple

หน้าตาจะคล้ายๆ Array แต่ว่าข้อดีของมันทำให้เราสามารถกำหนด Data type ที่จะเก็บลงใน Array ได้ว่าจะหน้าตาแบบไหนทำให้เราสามารถจัดการของใน Array ให้อยู่ในรูปแบบที่เป็น Fixed type ได้ อย่างเช่น

const arr1: [number, number] = [1, 2]
console.log(arr1) // [1, 2]
const arr2: [number, string, boolean] = [1, 'John Doe', true]

Enum

ตัวนี้ช่วยให้เราทำ Constant ได้ง่ายขึ้นมากเพราะปกติถ้าเป็น JS เราจะประกาศเป็นตัวแปรแบบ constant แบบนี้แทน

export const DATE_TIME_FORMAT_24H = 'yyyy-MM-dd HH:mm:ss'
export const VAT = 7 / 100

ที่นี้ลองมาเขียนเป็น enum บ้าง

enum DateTimeFormat {
DATE_TIME_24H = 'yyyy-MM-dd HH:mm:ss',
DATE_TIME_12H = 'yyyy-MM-dd hh:mm:ss',
DATE = 'yyyy-MM-dd'
}
enum FileTypeSupported {
jpg = 'jpg',
png = 'png',
mp4 = 'mp4'
}
console.log(FileTypeSupported.jpg) // jpg
enum Ans {
No,
Yes
}
console.log(Ans[0]) // No
console.log(Ans.NO) // 0
console.log(Ans[Ans.NO]) // No

Union

ตัวนี้ช่วยให้เราเลือกว่าตัวแปรเราจะมีการใช้ type อะไรได้บ้าง ที่อาจจะมากกว่า 1 การทำงานจะเหมือน Any แต่ Any จะรวมทุกตัว แต่ถ้าเราอยากให้มีแค่ 2 types ล่ะ?

let strOrNum: (number | string) = 1
strOrNum = 'test'
strOrNum = false // Errorlet moreThen2: (number | string | boolean) = false // ok

Function Declaration

function add(n1, n2) { 
return n1 + n2
}
// ES6 arrow
const add = (n1, n2) => n1 + n2

อย่างที่บอกไปว่า TypeScript เป็นภาษาที่ต้องประกาศ Type ด้วยดังนั้นการประกาศฟังก์ชันก็จะเหมือนกันการสร้างตัวแปรเพราะมันมี Pattern เดียวกัน

function add(n1: number, n2: number) {
return n1 + n2
}
// ES6 arrow
const add = (n1: number, n2: number) => n1 + n2

โค้ดทั้งหมดสามารถทำงานได้อย่างไม่เกิด Error ใดๆ แต่เมื่อเราประกาศ Type ให้กับ Input แล้วเราก็ควรประกาศให้กับ Output ด้วยเพื่อให้ Flow ของ Data ในระบบเป็น Type เดียวกันจนจบการทำงาน

function add(n1: number, n2: number): number {
return n1 + n2
}
const add = (n1: number, n2: number): number => n1 + n2

จะเห็นว่าผมใส่ return type ไปด้วยให้ออกมาเป็น number ซึ่งจะทำให้เราประกาศ function signature ได้ในที่นี้หมายถึงการทำให้ฟังก์ชันทำหน้าที่รับ input เป็นตัวเลข และส่งตัวเลขออกมาเท่านั้น เมื่อใดที่ n1 + n2 ได้ผลที่ไม่ใช้ number ตัว Complier จะแจ้งเราทันทีว่าเราทำผิด signature อยู่

เราสามารถใส่ void เข้าไปได้ถ้าต้องการให้ฟังก์ชันจะไม่มีการ return ค่าใดๆ ออกมา

function add(n1: number, n2: number): void {
return n1 + n2 // Error
}
const add = (n1: number, n2: number): void => n1 + n2 // Error--------------------------------------------------------
function
add(n1: number, n2: number): void {
console.log(n1 + n2) // ok
}
const add = (n1: number, n2: number): void => {
console.log(n1 + n2) // ok
}

ทำไมต้องใส่ return type ทั้งๆ ที่ไม่ใส่ก็ได้? สาเหตุเลยมันช่วยให้การอ่านโค้ดทำได้ง่ายขึ้นเพราะเราจะสามารถคาดการผลลัพธ์ของฟังก์ชันที่จะออกมาได้ว่าเป็นอะไร ก่อนที่เราจะทำการรันโค้ด เราอาจจะเคยเจอว่ามีคนถามว่าฟังก์ชันนี้ return อะไรออกมา JSON?, String?, Int? มันทำให้เรารู้ได้เลยว่า เราไม่ได้เขียนฟังก์ชันที่ส่ง Bug ออกก็คือส่งค่ามาผิด Type นั้นเอง และยังช่วยให้การอ่านโค้ดหาว่าบรรทัดไหนทำให้ Signature ของฟังก์ชันเปลี่ยนไป ก็จะได้แก้ถูกจุด

Optional parameter

เราสามารถทำให้ฟังก์ชันเรารับหรือไม่รับ Args ที่ประกาศไว้ก็ได้ด้วยทำเป็น Optional ไว้ เพียงแค่ใส่ ? ไว้ที่ตัวแปร

const add = (n1: number, n2: number): number => n1 + n2
add(1) // Error
const add = (n1: number, n2?: number): number => n1 + n2add(1) // ok got NaN

Default value

การทำ Optional อีกรูปแบบนึงคือการใส่ Default value เพื่อป้องกันตัวแปรเราจะเป็น undefined และทำให้การทำงานผิดพลาด เพราะดูจากโค้ดด้านบนเราจะเห็นว่า n1 + undefined ผลที่ได้คือ NaN ซึ่งหมายความระบบจะบัคตรงนี้ เพื่อเลี่ยงอะไรแบบนี้ผมขอแนะนำท่านี้

const add = (n1: number, n2: number = 2): number => n1 + n2add(1) // 3

เพียงแต่ลบ ? ออก แล้วเราก็ใส่ค่า default เข้าไปแทนเท่านี้ฟังก์ชันเราก็ทำงานได้แล้ว

Interface

Function arg input

interface IPerson { 
firstName: string
lastName: string
}
function sayHiToPerson(info: IPerson) {
return `Hi ${info.firstName} ${info.lastName}`
}
sayHiToPerson({ firstName: "John", lastName: "Doe" }) // Hi John DoesayHiToPerson({ lastName: "Doe" }) // Error missing firstNamesayHiToPerson({ test: 1 }) // Error missing wrong interface

เราสามารถประกาศหน้าตาของ input ได้ว่าต้องการให้เป็นแบบไหนโดยการสร้าง interface ขึ้นมา จากนั้นจึงมาประกาศเป็น type ของ input หากมีการส่งที่ไม่อยู่ใน interface มาเป็น parameter ระบบก็จะแจ้ง Error ทันที

Function output

interface IPerson { 
firstName: string
lastName: string
}
function createPerson(firstName: string, lastName: string): IPerson {
return { firstName, lastName };
}
console.log(createPerson("John", "Doe"));
// { firstName: 'John', lastName: 'Doe'}

เราจะสามารถทำให้ output ของฟังก์ชันเราถูกกำหนดด้วย interface ได้เพื่อการันตีตัว signature ว่าจะออกมาเป็น Object ที่มี firstName และ lastName เท่านั้น ถ้าเกิดเราดันไปเขียนให้ส่วนของ return เป็น

return { firstName, lastName, age: 10 };

แบบนี้จะ Error ทันทีเพราะใน Interface ไม่มี age อยู่

Class interface

interface Person {
fullName: string;
toString();
}
class Employee implements Person {
empID: string;
fullName: string;
constructor (eID: string, name: string) {
this.empID = eID;
this.fullName = name;
}
toString() {
console.log(`EMP ID of ${this.fullName} is ${this.empID}`);
}
}
const emp1 = new Employee('E11', 'John Doe')
console.log(emp1);
// Employee { empID: 'E11', fullName: 'John Doe' }
console.log(emp1.toString()) // EMP ID of John Doe is E11

เราสามารถทำ Interface ให้ class ได้อีกด้วยถ้าหากใครเขียน OOP เป็นหลัก

Interface optional

interface IPerson { 
firstName?: string
lastName: string
}

เราสามารถใช้ ? เพื่อเป็นการบอกว่าไม่จำต้องรับ parameter ตัวไหนบ้างเหมือนกับการประกาศฟังก์ชันก่อนหน้านี้ก็ได้

Default value

ตัว Interface ไม่สามารถทำ Default value ได้ครับ เพราะมันเป็น Abstract layer ไม่ใช่ส่วนของ Implement layer เพราะงั้นเราต้องไปทำเองใส่ส่วนของ Implement layer ก็คือพวกที่เรียกใช้งาน Interface นั้น และการทำ default value นั้นเราจะต้องทำคู่ไปกับการใช้ destructuring และการทำ optional เดี๋ยวผมจะทำให้ดู

interface IPerson { 
firstName?: string
lastName: string
}
function sayHi({ firstName = 'Default' , lastName }: IPerson): any {
console.log(`Hi ${firstName} ${lastName}`);
}
sayHi({ lastName: 'Doe'}) // Hi Default Doe

ผมทำให้ firstName เป็น optional ไว้ก่อน ถ้าไม่งั้นมันจะฟ้อง Error ว่า firstName หายไป จากนั้นผมก็ใช้ทำการ destructuring ตัว Object ใน sayHi เพื่อเอาค่าที่ต้องการจาก Interface พูดง่ายๆว่า ก็คือผมแกะ firstName และ lastName ออกมาแล้วใส่ Default ให้กับตัว firstName เพราะมันเป็น Optional ไปแล้วถ้าเกิดลืมใส่มาระบบเราก็จะไม่พัง และส่วนบรรทัดถัดไป

sayHi({ lastName: 'Doe'})

ผมก็โยนค่าเข้าไปแต่ไม่ใส่ firstName แล้วตัวระบบจะรู้เองว่ามันไม่มีค่ามา จากนั้นมันก็จะเอาค่าที่ใส่เป็น default ไว้มายัดให้แทน ตรงนี้ไม่ใช่การทำงานของ TS แต่มันการเขียนโปรแกรมทั่วไปของ ES6 ที่เราเอามาประยุกต์ให้เข้ากับตัว TS แค่นั้นเองครับ สำหรับคนที่ไม่เข้าใจเรื่อง destructuring แนะนำว่าไปอ่านจาก developer.mozilla ครับ สำหรับประโยชน์การเรียก method โดยการโยน Object เข้าไปแทนการเรียกแบบปกติ ก็คือ sayHi(arg1, arg2, .... argN) และทำไมเราควรเขียน JS ให้รับ Object แทนนั้นไว้บทความหน้าผมจะมาเล่าให้ฟังนะครับ เดี๋ยวจะเอาลิ้งมาแปะให้ในนี้

หน้าที่ของ interface ยังไม่หมดเท่านี้ ถ้าสนใจลองไปอ่านต่อที่ devblogs.microsoft

ES6 Feature

How to migrate

  • เปลี่ยนชื่อไฟล์ .js เป็น .ts
  • ใส่ไฟล์ tsconfig.json (link)
  • ใส่ Type ให้กับตัวแปร
  • เช็ค Error ที่เกิดจาก Type ผิด อันไหนไม่แน่ใจ type ใส่ any ไปก่อน
  • มีเวลาก็มาทำ Interface ให้เพื่อทำ Function signature

คร่าวๆ คงมีประมาณนี้ครับ

สำหรับมือใหม่ในการหัดเขียน TS ผมก็คิดว่าเท่านี้ก็พอทำงานได้แล้วครับ เพราะคำสั่งที่เหลือจะเหมือนกับ JS ทุกอย่างเลย สำหรับบทความนี้ผมจะไม่ลงลึกไปมากในตัวของ Feature ของ TS เพราะ ถ้าใครจะอ่านต่อ ผมแนะนำว่าลองศึกษาที่เว็บหลักของ TS ที่ www.typescriptlang.org ได้เลยครับ

AlgorithmTut

May the force be with you. **Tut stand for Tutorial**

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store