TypeScript เป็นภาษาคอมพิวเตอร์ที่เป็น open-source อีกภาษาหนึ่ง ที่พัฒนาโดยบริษัทไมโครซอฟต์ เป็นภาษาที่เป็นแบบ High-Level และแตกต่างจากภาษาอื่นๆ ที่จะ Compile เป็น Machine Code แต่ TypeScript compile เป็นภาษา JavaScript

เนื่องจาก JavaScript เป็นภาษาที่เป็น Interpreted Language ที่ไม่ต้องทำการ Compile ก่อน Run เช่นเดียวกันกับ Python , PHP อื่นๆ ดังนั้น ต้องรันก่อนถึงจะรู้ว่ามี Error

TypeScript จะเป็น Type System เพิ่มเติมของ JavaScript และเป็นการเอา Features ในอนาคตของ JavaScript มาพัฒนาก่อน

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

JavaScript and TypeScript

JavaScriptTypeScript
Type SystemDynamically typedStatically typed
CompilationInterpretedCompiled
Type AnnotationsNot requiredRequired
ToolingLimited type checking supportRich type checking support
Code QualityMore prone to errors and bugs due to dynamic typingLess prone to errors and bugs due to static typing
Development SpeedFast due to flexibility of dynamic typingSlower due to required type annotations and compilation step
MaintainabilityMay be harder to maintain and refactor as codebase growsEasier to maintain and refactor due to better type checking and tooling support

Setup

Tutorial เริ่ม project ด้วย:

tsc --int

รันด้วย watch mode เพื่ออ่านไฟล์ tsconfig.json ด้วย

tsc -w

Declaration

การกำหนดค่าตัวแปรขณะที่ประกาศตัวแปร อย่าง var กับ let ไม่จำเป็นที่จะต้องกำหนดค่าตัวแปรตอนประกาศก็ได้ แต่ว่า const นั้นจำเป็นที่จะต้องกำหนดต่าตัวแปรให้มัน

Declaration TypeScopeDeclarationUpdate valueRe-declare
varglobal, functionWithout initializeYesYes
constblockNeed initializeNoNo
letblockWithout initializeYesNo

แนะนำให้ประกาศตัวแปรด้วย const เสมอและค่อยเปลี่ยนเป็น let ถ้าคุณต้องการ เปลี่ยนแปลงค่า (mutate) หรือ assign ค่าให้ตัวแปรในภายหลัง

Scope

กำหนดระดับการเข้าถึงของตัวแปรนั้นๆ

  • Global Scope: ประกาศแบบ global ข้างนอก function/class ใดๆ
  • Function Scope: ประกาศและเข้าถึงได้ใน function/class
  • Block Scope: ประกาศและเข้าถึงได้ใน code block (ส่วนที่ปิดด้วย () เช่น if/try/catch/while)

Basic Types

TypeScript: Documentation

// 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'

Any

การใช้ข้อมูลชนิด any เป็นสิ่งที่ควรหลีกเลี่ยง เพราะมันไม่ถูก type-check และสามารถรับข้อมูลชนิดไหนมาก็ได้

let hero
 
function getHero() {
  return "Batman"
}
 
hero = getHero() // any

ควรประกาศ type เพื่อระบุชนิดอย่างชัดเจน

let hero: string
 
function getHero() {
  return "Batman"
}
 
hero = getHero() // string

Object

Objects are variables too. But objects can contain many values.

const car: { type: string, model: string, year: number } = {
  type: "Toyota",
  model: "Corolla",
  year: 2009
}
 
// The parameter's type annotation is an object type
function printCoord(pt: { x: number; y: number }) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
}
printCoord({ x: 3, y: 7 });

Type Alias

  • Type Aliases allow defining types with a custom name (an Alias).
  • Type Aliases can be used for primitives like string or more complex types such as objects and arrays:
type Point = {
  x: number;
  y: number;
};
 
// Exactly the same as the earlier example
function printCoord(pt: Point) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
}
 
printCoord({ x: 100, y: 100 });

Readonly and Optional:

type User = {
  readonly _id: string // readonly
  name: string
  age: number
  isActive: true // literal type
  creditcard?: number // optional
}

Reuse types:

type cardNumber = {
  cardNumber: string
}
 
type cardHolder = {
  cardHolder: string
}
 
type cardDetail = cardNumber &
  cardHolder & {
    cvv: number
  }

Interface

เป็นวิธีการกำหนดประเภทข้อมูลให้กับออบเจ็คแบบนามธรรมเพื่อที่จะระบุ Property ที่ออบเจ็คสามารถมีและประเภทข้อมูลที่ Property จะมีได้

interface User {
  dbId: number
  name: string
  age: number
}
 
const user01: User = {
  dbId: 1,
  name: "John",
  age: 20,
}

Interface สามารถสืยทอดได้โดยใช้ extends

interface User {
  dbId: number
  name: string
  age: number
}
 
interface UserWithAddress extends User {
  address: string
}

Type vs Interface

Read More สำหรับการกำหนดรูปร่างหน้าตาของออปเจกต์นั้น สามารถใช้ได้ทั้ง type และ interface

interface Point {
  x: number
  y: number
}
 
type Point = {
  x: number
  y: number
}

สังเกตว่าการใช้ interface จะคล้ายกับการสร้าง Class และการใช้ type จะคล้ายการประกาศค่าของตัวแปรโดยใช้ = เพราะฉะนั้นหากเป็นกรณีที่ตั้งชื่อให้กับ Type มาตรฐานทั่วไป หรือทำ Literal Types จะไม่สามารถใช้ interface ได้

type Color = "Yellow" | "Red" // OK
 
interface Color "Yellow" | "Red" // ❌ ไม่ได้ เพราะไม่ใช่ออบเจกต์

ถ้าใช้ interface ได้ (เช่นกำหนดสำหรับ Object, Class) ก็ใช้ ถ้าทำไม่ได้ค่อยใช้ type แทน

Union

หลายครั้งที่ Type ที่สร้างขึ้นมาจะไม่ครอบคลุมการใช้งานได้ทั้งหมด หรือมีความต้องการที่จะใช้ตัวแปรเก็บค่าที่มีความซับซ้อนมากขึ้น เราสามารถใช้ Union Types และ Intersection Types มาช่วยได้

type ID = string | number
 
const a: ID = "abc"
const b: ID = 101

อีกตัวอย่างหนึ่งที่จะได้ใช้ Union Type เช่น Literal Types

type Color = "red" | "green" | "blue"
 
function printColorCode(c: Color): string {
  switch (c) {
    case "red":
      return "#FF0000"
    case "green":
      return "#00FF00"
    case "blue":
      return "#0000FF"
}

ประกาศ Union Type ให้กับ array ให้ใส่ได้ทั้ง number และ string

const data1: number[] = [1, 2, 3]
const data2: string[] = ["1", "2", "3"]
const data3: (number | string)[] = [1, "2", 3]
 
const data4: number[] | string[] = [1, 2, 3] // or ["1", "2", "3"]

Intersection

จะใช้กับ Interface จะเป็นเสมือนการ “ต่อ” Interface ต่างๆ เข้าด้วยกัน

interface Colorful {
  color: string
}
 
interface Circle {
  radius: number
}
 
interface Square {
  size: number
}
 
interface Measurable {
  area: () => number
}
 
// มี color, radius และฟังก์ชั่น area
type ColorfulCircle = Colorful & Circle & Measurable
 
// มี color, size และฟังก์ชั่น area
type ColorfulSquare = Colorful & Square & Measurable

Array

An array is a special variable, which can hold more than one value:

// Type 1
const superHero: string[] = []
superHero.push("Batman")
 
// Type 2
const heroPower: Array<number> = []
heroPower.push(9000)
 
// Array of User type
type User = {
  name: string
  age: number
}
const user: User[] = []
 

Tuple

หมายถึงข้อมูลที่มีโครงสร้างแบบ array แต่ระบุจำนวนข้อมูลที่แน่นอนพร้อมกำหนดชนิดข้อมูลของแต่ละช่องไว้อย่างเรียบร้อย

const somchai: [name: string, age: number] = ["Somchai", 24]
 
const rgb: [number, number, number] = [255, 255, 255]

Enum

enum ใช้ในการประกาศค่าคงที่ และรวมค่าคงที่ ที่เราต้องการไว้ในกลุ่มเดียวกัน โดยไม่จำเป็นต้องประกาศแยก const แต่ละตัวออกมาเช่น

enum Role {
  ADMIN,
  READ_ONLY,
  AUTHOR,
}
 
const person = {
  name: "Maximilian",
  role: Role.ADMIN,
}

Function

Function with default paremeter

function signUpUser(name: string, isPaid: boolean = false) {
  // ...
}

Function return 1 value

function sumNumbers(a: number, b: number): number {
  return a + b;
}

Arrow Function

Arrow function คือการสร้างฟังก์ชันรูปแบบใหม่ที่สั้นกระชับกว่าเดิม

let loginUsers = (email: string, password: string) => {
  // ...
}

Example

const getHello = (s: string): string => {
  return `Hello ${s}`
}
 
const heros = ["Thor", "Ironman", "Hulk"]
 
heros.map((heros: string): string => {
  return getHello(heros)
})

Class

คือแม่แบบที่ใช้สำหรับสร้างออบเจ็ค ประกอบไปด้วยการกำหนด attribute และ method ที่ออบเจ็คจะมี ใช้แนวคิดของการนำโค้ดกลับมาใช้ซ้ำ (Code-reuse) สามารถสืบทอดคลาส (Inheritance)

Interface เหมือนบอกลักษณะของแม่พิมพ์ Class เหมือนแม่พิมพ์ในการสร้างวัตถุที่ออกแบบโดย interface Object หรือ instance คือผลลัพธ์ที่ถูกสร้างขึ้นโดยแม่พิมพ์

class Rectangle {
    width: number;
    height: number;
 
    area(): number {
        return this.width * this.height;
    }
}
 
let rect: Rectangle = new Rectangle();
rect.width = 3;
rect.height = 4;
 
console.log(`Area of rect: ${rect.area()}`);

Private/ Public/ Protected

ใช้กำหนดระดับการเขาถึงของ attribute

class User {
  public name: string // public is default
  email: string
  private city: string = ""
  constructor(name: string, email: string) {
    this.name = name
    this.email = email
  }
}
 
const user = new User("John", "[email protected]")

Alternative

class User {
  constructor(
    public name: string, // public is default
    public email: string,
    private city: string = ""
  ) {}
}

Getter/ Setter

class User {
  constructor(
    public name: string, // public is default
    public email: string,
    private city: string = ""
  ) {}
 
  get getInfo(): string {
    return `Name: ${this.name} | Email: ${this.email} | City: ${this.city}`
  }
 
  set setCity(city: string) {
    this.city = city
  }
}

Inheritance

class SubUser extends User {
  isFamily: boolean = true
}

Implement

interface TakePhoto {
  camaraMode: string
  filters: string
  burst: number
}
 
class Instagram implements TakePhoto {
  constructor(
    public camaraMode: string,
    public filters: string,
    public burst: number,
    public short: boolean // Add new property
  ) {}
}

Generic

เป็นการยืดหยุ่นในการรับค่า parameter

function identity<T>(val: T): T { // T is a type variable
  return val
}
 
identity<string>("hello")
identity(10)
identity(true)
 
// Using Generics with Interfaces
interface bottle {
  name: string
  price: number
}
 
identity<bottle>({ name: "water", price: 10 })

Generics in Array

// Regular function
function getSearchProducts<T>(products: T[]): T {
  const myIndex = 3
  return products[myIndex]
}
 
// Arow function with generics
const getSearchProducts2 = <T>(products: T[]): T => {
  const myIndex = 3
  return products[myIndex]
}

Generics with Class

interface Quiz {
  name: string
  type: string
}
 
interface Course {
  name: string
  author: string
  subject: string
}
 
class sellableItem<T> {
  public cart: T[] = []
 
  addToCart(product: T) {
    this.cart.push(product)
  }
}

Type Narrowing

Documentation - Narrowing

function detectType(val: number | string) {
  if (typeof val === "string") {
    return val.toLowerCase()
  }
  return val + 1
}
 
function provideId(id: string | null) {
  if (!id) {
    console.log("Please provide an id")
    return
  }
  id.toLowerCase()
}

Reference: