ผลต่างระหว่างรุ่นของ "Se63/typescript/started"

จาก Theory Wiki
ไปยังการนำทาง ไปยังการค้นหา
 
(ไม่แสดง 8 รุ่นระหว่างกลางโดยผู้ใช้คนเดียวกัน)
แถว 1: แถว 1:
 +
: ''หมายเหตุ ใน Typescript Playground จะมี tab size เป็น 4 เราจะคงไว้ในเอกสารนี้ แต่หลัง ๆ ในโค้ดของเราจะใช้ tab size เป็น 2''
 +
 
สิ่งที่ TypeScript เพิ่มให้กับ javascript คือระบบ type (type system) ที่ยืดหยุ่น และช่วยในการตรวจสอบโปรแกรม
 
สิ่งที่ TypeScript เพิ่มให้กับ javascript คือระบบ type (type system) ที่ยืดหยุ่น และช่วยในการตรวจสอบโปรแกรม
  
แถว 4: แถว 6:
  
 
<syntaxhighlight lang="typescript">
 
<syntaxhighlight lang="typescript">
function inSideCircle(circle, point) {
+
function insideCircle(circle, point) {
  let dx = (circle.x - point.x);
+
    let dx = (circle.x - point.x);
  let dy = (circle.y - point.y);
+
    let dy = (circle.y - point.y);
  return (circle.r * circle.r) >= (dx * dx + dy * dy);
+
    return (circle.r * circle.r) >= (dx * dx + dy * dy);
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
ทดลองนำไปใส่ใน typescript playground  จะเห็นการเตือนว่าทั้ง circle และ point มี type เป็น <tt>any</tt> (นั่นคือเป็นอะไรก็ได้)
+
ทดลองนำไปใส่ใน [https://www.typescriptlang.org/play/ typescript playground] จะเห็นการเตือนว่าทั้ง circle และ point มี type เป็น <tt>any</tt> (นั่นคือเป็นอะไรก็ได้)
  
 
เพิ่มบรรทัดต่อไปนี้
 
เพิ่มบรรทัดต่อไปนี้
  
 
<syntaxhighlight lang="typescript">
 
<syntaxhighlight lang="typescript">
console.log(inSideCircle({ r: 20 }, {}));
+
console.log(insideCircle({ r: 20 }, {}));
 
</syntaxhighlight>
 
</syntaxhighlight>
  
แถว 22: แถว 24:
  
 
สังเกตว่าโปรแกรมสามารถทำงานได้และมีผลลัพธ์ แต่นี่เป็นสิ่งที่เราต้องการหรือเปล่า?  จากตัวอย่างนี้ อาจจะพอเห็นได้ว่ามีการส่งข้อมูลผิดพลาด แต่ถ้าโค้ดส่วนนี้อยู่ในโปรแกรมใหญ่ ๆ เราจะไม่ทราบเลยว่าอาจจะมีความผิดพลาดมาจากการส่งค่าจากที่อื่น เช่น ใช้ตัวแปรผิดตัว (หรือพิมพ์ชื่อผิด) เป็นต้น
 
สังเกตว่าโปรแกรมสามารถทำงานได้และมีผลลัพธ์ แต่นี่เป็นสิ่งที่เราต้องการหรือเปล่า?  จากตัวอย่างนี้ อาจจะพอเห็นได้ว่ามีการส่งข้อมูลผิดพลาด แต่ถ้าโค้ดส่วนนี้อยู่ในโปรแกรมใหญ่ ๆ เราจะไม่ทราบเลยว่าอาจจะมีความผิดพลาดมาจากการส่งค่าจากที่อื่น เช่น ใช้ตัวแปรผิดตัว (หรือพิมพ์ชื่อผิด) เป็นต้น
 +
 +
== Interface ==
 +
 +
เราสามารถระบุว่า object ที่จะส่งให้ฟังก์ชัน หรือตัวแปรต่าง ๆ ต้องมี '''interface''' ตามที่ต้องการได้โดยประกาศ <tt>interface</tt> (อ่านเพิ่มได้ที่ [https://www.typescriptlang.org/docs/handbook/interfaces.html handbook]) ดังนี้
 +
 +
<syntaxhighlight lang="typescript">
 +
interface Circle {
 +
    x: number;
 +
    y: number;
 +
    r: number;
 +
}
 +
 +
interface Point {
 +
    x: number;
 +
    y: number;
 +
}
 +
</syntaxhighlight>
 +
 +
และเพิ่ม type ให้กับพารามิเตอร์ของ <tt>insideCircle</tt>  ให้ทดลองเพิ่ม type ให้กับทีละพารามิเตอร์แล้วสังเกต error ที่ระบบระบุในบรรทัดที่เรียกใช้ <tt>insideCircle</tt>
 +
 +
<syntaxhighlight lang="typescript">
 +
function insideCircle(circle: Circle, point: Point) {
 +
    // ...
 +
}
 +
</syntaxhighlight>
 +
 +
=== optional และ readonly ===
 +
เราสามารถระบุว่า property บางอันจะมีหรือไม่มีก็ได้ โดยตามด้วยเครื่องหมาย ? เช่น
 +
 +
<syntaxhighlight lang="typescript">
 +
interface Circle {
 +
    x: number;
 +
    y: number;
 +
    r: number;
 +
    color?: number;
 +
}
 +
</syntaxhighlight>
 +
 +
สามารถระบุว่าอ่านอย่างเดียวได้ด้วย keyword <tt>readonly</tt>
 +
 +
การตรวจสอบว่าข้อมูลที่ส่งตรงตาม interface มีรายละเอียดมากกว่านี้มาก  สามารถอ่านเพิ่มที่ [https://www.typescriptlang.org/docs/handbook/interfaces.html handbook]
 +
 +
== Class ==
 +
 +
สังเกตว่าฟังก์ชัน <tt>insideCircle</tt> ทำงานกับวงกลมโดยเฉพาะ เราสามารถนำฟังก์ชันนี้ไปใส่รวมกับ '''สิ่งที่ควรจะเป็น <tt>Circle</tt>'''  ในกรณีนี้ interface จะไม่ใช่สิ่งที่เราต้องการเท่าใดนัก เพราะว่า interface ใช้ระบุ "หน้าตา" ว่า type ควรทำอะไรได้ ไม่ใช่ระบุการทำงานจริง ๆ  เราจะเปลี่ยน interface Circle เป็น class Circle ดังนี้
 +
 +
ในการทดลองเขียน ให้ป้อนโดยละ constructor ไว้ก่อน แล้วดู error ที่ระบบแสดงออกมา (จะอยู่ที่บรรทัด x,y,r)
 +
 +
<syntaxhighlight lang="typescript">
 +
interface Point {
 +
    x: number;
 +
    y: number;
 +
}
 +
 +
class Circle {
 +
    x: number;
 +
    y: number;
 +
    r: number;
 +
    constructor(xx: number, yy: number, rr: number) {
 +
        this.x = xx;
 +
        this.y = yy;
 +
        this.r = rr;
 +
    }
 +
 +
    inside(point: Point): boolean {
 +
        let dx = (this.x - point.x);
 +
        let dy = (this.y - point.y);
 +
        return (this.r * this.r) >= (dx * dx + dy * dy);
 +
    }
 +
}
 +
 +
let c = new Circle(10, 20, 30);
 +
 +
console.log('Circle is at', c.x, c.y);
 +
console.log(c.inside({ x: 10, y: 40 }));
 +
</syntaxhighlight>
 +
 +
ในคลาส เราจะประกาศเมทอด <tt>inside</tt> (ซึ่งถูกเรียกในตอนท้าย) และเมทอด constructor ที่ทำหน้าที่หลักคือกำหนดค่าให้กับ property ของวัตถุเมื่อมีการสร้างขึ้น  เราจะสร้าง object ด้วย <tt>new</tt> พร้อมกับส่งพารามิเตอร์กำหนดค่าเริ่มต้น
 +
 +
สังเกตว่า property x, y, r สามารถอ่านค่าได้จาก object c
 +
 +
การใช้ constructor ในการกำหนดค่าเริ่มต้นทำบ่อยจนกระทั่ง typescript ให้เราเขียนแบบนี้ได้
 +
 +
<syntaxhighlight lang="typescript">
 +
class Circle {
 +
    constructor(public x: number, public y: number, public r: number) {}
 +
 +
    // ...
 +
}
 +
</syntaxhighlight>
 +
 +
keyword <tt>public</tt> ทำให้เรานิยาม property และให้นำค่าเริ่มต้นมาจากพารามิเตอร์ได้เลย
 +
 +
=== private และ protected ===
 +
 +
เราสามารถปิดการเข้าถึง property ได้โดยระบุ keyword <tt>private</tt> แทน <tt>public</tt> ที่ property  ถ้าแก้บรรทัด constructor เป็น
 +
 +
<syntaxhighlight lang="typescript">
 +
class Circle {
 +
    constructor(private x: number, private y: number, private r: number) {}
 +
 +
    // ...
 +
}
 +
</syntaxhighlight>
 +
 +
บรรทัดที่พิมพ์ <tt>c.x</tt> จะขึ้น error
 +
 +
== Inheritance ==
 +
 +
เราสามารถขยายความสามารถของคลาสโดยการ extend เช่น
 +
 +
<syntaxhighlight lang="typescript">
 +
class ExpandableCircle extends Circle {
 +
    expand(s: number) {
 +
        this.r += s;
 +
    }
 +
}
 +
</syntaxhighlight>
 +
 +
object ที่เป็น instance ของ ExpandableCircle จะเป็น Circle อยู่ และสามารถขยายได้ โดยสั่งเช่น obj.expand(10)
 +
 +
เราจะได้เห็นการใช้งานในตัวอย่าง zombie ที่จะเขียนถัดไป
 +
 +
== เรื่องอื่น ๆ ==
 +
 +
=== Type alias ===
 +
แทนที่จะสร้าง interface เราสามารถกำหนดชื่อให้กับ type ได้โดยตรง เช่น
 +
 +
<syntaxhighlight lang="typescript">
 +
type Name = string;
 +
type Person = {
 +
    firstName: string;
 +
    lastName: string;
 +
};
 +
</syntaxhighlight>
 +
 +
ลักษณะการใช้งานจะคล้ายกับ interface แต่มีความแตกต่างอยู่  เมื่อทำงานกับ react เราจะใช้ type alias เวลาระบุ type ของ props

รุ่นแก้ไขปัจจุบันเมื่อ 02:57, 13 กรกฎาคม 2563

หมายเหตุ ใน Typescript Playground จะมี tab size เป็น 4 เราจะคงไว้ในเอกสารนี้ แต่หลัง ๆ ในโค้ดของเราจะใช้ tab size เป็น 2

สิ่งที่ TypeScript เพิ่มให้กับ javascript คือระบบ type (type system) ที่ยืดหยุ่น และช่วยในการตรวจสอบโปรแกรม

ลองดูตัวอย่างฟังก์ชันที่ตรวจสอบว่าจุดอยู่ในวงกลมหรือไม่

function insideCircle(circle, point) {
    let dx = (circle.x - point.x);
    let dy = (circle.y - point.y);
    return (circle.r * circle.r) >= (dx * dx + dy * dy);
}

ทดลองนำไปใส่ใน typescript playground จะเห็นการเตือนว่าทั้ง circle และ point มี type เป็น any (นั่นคือเป็นอะไรก็ได้)

เพิ่มบรรทัดต่อไปนี้

console.log(insideCircle({ r: 20 }, {}));

แล้วลองเรียกใช้งาน จะพบผลลัพธ์ที่ console ของ browser (ให้ลองหาวิธีแสดง console ดู อาจจะกด inspect element ก่อน แล้วค่อยเลือก console ก็ได้)

สังเกตว่าโปรแกรมสามารถทำงานได้และมีผลลัพธ์ แต่นี่เป็นสิ่งที่เราต้องการหรือเปล่า? จากตัวอย่างนี้ อาจจะพอเห็นได้ว่ามีการส่งข้อมูลผิดพลาด แต่ถ้าโค้ดส่วนนี้อยู่ในโปรแกรมใหญ่ ๆ เราจะไม่ทราบเลยว่าอาจจะมีความผิดพลาดมาจากการส่งค่าจากที่อื่น เช่น ใช้ตัวแปรผิดตัว (หรือพิมพ์ชื่อผิด) เป็นต้น

Interface

เราสามารถระบุว่า object ที่จะส่งให้ฟังก์ชัน หรือตัวแปรต่าง ๆ ต้องมี interface ตามที่ต้องการได้โดยประกาศ interface (อ่านเพิ่มได้ที่ handbook) ดังนี้

interface Circle {
    x: number;
    y: number;
    r: number;
}

interface Point {
    x: number;
    y: number;
}

และเพิ่ม type ให้กับพารามิเตอร์ของ insideCircle ให้ทดลองเพิ่ม type ให้กับทีละพารามิเตอร์แล้วสังเกต error ที่ระบบระบุในบรรทัดที่เรียกใช้ insideCircle

function insideCircle(circle: Circle, point: Point) {
    // ...
}

optional และ readonly

เราสามารถระบุว่า property บางอันจะมีหรือไม่มีก็ได้ โดยตามด้วยเครื่องหมาย ? เช่น

interface Circle {
    x: number;
    y: number;
    r: number;
    color?: number;
}

สามารถระบุว่าอ่านอย่างเดียวได้ด้วย keyword readonly

การตรวจสอบว่าข้อมูลที่ส่งตรงตาม interface มีรายละเอียดมากกว่านี้มาก สามารถอ่านเพิ่มที่ handbook

Class

สังเกตว่าฟังก์ชัน insideCircle ทำงานกับวงกลมโดยเฉพาะ เราสามารถนำฟังก์ชันนี้ไปใส่รวมกับ สิ่งที่ควรจะเป็น Circle ในกรณีนี้ interface จะไม่ใช่สิ่งที่เราต้องการเท่าใดนัก เพราะว่า interface ใช้ระบุ "หน้าตา" ว่า type ควรทำอะไรได้ ไม่ใช่ระบุการทำงานจริง ๆ เราจะเปลี่ยน interface Circle เป็น class Circle ดังนี้

ในการทดลองเขียน ให้ป้อนโดยละ constructor ไว้ก่อน แล้วดู error ที่ระบบแสดงออกมา (จะอยู่ที่บรรทัด x,y,r)

interface Point {
    x: number;
    y: number;
}

class Circle {
    x: number;
    y: number;
    r: number;
    constructor(xx: number, yy: number, rr: number) {
        this.x = xx;
        this.y = yy;
        this.r = rr;
    }

    inside(point: Point): boolean {
        let dx = (this.x - point.x);
        let dy = (this.y - point.y);
        return (this.r * this.r) >= (dx * dx + dy * dy);
    }
}

let c = new Circle(10, 20, 30);

console.log('Circle is at', c.x, c.y);
console.log(c.inside({ x: 10, y: 40 }));

ในคลาส เราจะประกาศเมทอด inside (ซึ่งถูกเรียกในตอนท้าย) และเมทอด constructor ที่ทำหน้าที่หลักคือกำหนดค่าให้กับ property ของวัตถุเมื่อมีการสร้างขึ้น เราจะสร้าง object ด้วย new พร้อมกับส่งพารามิเตอร์กำหนดค่าเริ่มต้น

สังเกตว่า property x, y, r สามารถอ่านค่าได้จาก object c

การใช้ constructor ในการกำหนดค่าเริ่มต้นทำบ่อยจนกระทั่ง typescript ให้เราเขียนแบบนี้ได้

class Circle {
    constructor(public x: number, public y: number, public r: number) {}

    // ...
}

keyword public ทำให้เรานิยาม property และให้นำค่าเริ่มต้นมาจากพารามิเตอร์ได้เลย

private และ protected

เราสามารถปิดการเข้าถึง property ได้โดยระบุ keyword private แทน public ที่ property ถ้าแก้บรรทัด constructor เป็น

class Circle {
    constructor(private x: number, private y: number, private r: number) {}

    // ...
}

บรรทัดที่พิมพ์ c.x จะขึ้น error

Inheritance

เราสามารถขยายความสามารถของคลาสโดยการ extend เช่น

class ExpandableCircle extends Circle {
    expand(s: number) {
        this.r += s;
    }
}

object ที่เป็น instance ของ ExpandableCircle จะเป็น Circle อยู่ และสามารถขยายได้ โดยสั่งเช่น obj.expand(10)

เราจะได้เห็นการใช้งานในตัวอย่าง zombie ที่จะเขียนถัดไป

เรื่องอื่น ๆ

Type alias

แทนที่จะสร้าง interface เราสามารถกำหนดชื่อให้กับ type ได้โดยตรง เช่น

type Name = string;
type Person = {
    firstName: string;
    lastName: string;
};

ลักษณะการใช้งานจะคล้ายกับ interface แต่มีความแตกต่างอยู่ เมื่อทำงานกับ react เราจะใช้ type alias เวลาระบุ type ของ props