ประเภทและรหัสที่ทดสอบได้ เป็นสองวิธีที่มีประสิทธิภาพที่สุดในการหลีกเลี่ยงข้อบกพร่องโดยเฉพาะอย่างยิ่งเมื่อรหัสเปลี่ยนแปลงไปตามกาลเวลา เราสามารถนำสองเทคนิคนี้ไปใช้กับการพัฒนา JavaScript โดยใช้ประโยชน์จากรูปแบบการออกแบบ TypeScript และรูปแบบการฉีดแบบพึ่งพา (DI) ตามลำดับ
เทมเพลตบูตสแตรปคืออะไร
ในบทช่วยสอน TypeScript นี้เราจะไม่กล่าวถึงพื้นฐานของ TypeScript โดยตรงยกเว้นการคอมไพล์ แต่เราจะสาธิตแนวทางปฏิบัติที่ดีที่สุดของ TypeScript แทนในขณะที่เราแนะนำวิธีการสร้างบอท Discord ตั้งแต่เริ่มต้นเชื่อมต่อการทดสอบและ DI และสร้างบริการตัวอย่าง เราจะใช้:
ก่อนอื่นมาสร้างไดเรกทอรีใหม่ชื่อ typescript-bot
จากนั้นป้อนและสร้างโครงการ Node.js ใหม่โดยเรียกใช้:
npm init
หมายเหตุ: คุณสามารถใช้ yarn
สำหรับสิ่งนั้น แต่เราจะยึดตาม npm
เพื่อความกะทัดรัด
สิ่งนี้จะเปิดตัวช่วยสร้างแบบโต้ตอบซึ่งจะตั้งค่า package.json
ไฟล์. คุณสามารถกดได้อย่างปลอดภัย ป้อน สำหรับคำถามทั้งหมด (หรือให้ข้อมูลบางอย่างหากคุณต้องการ) จากนั้นมาติดตั้งการอ้างอิงและการอ้างอิงเบี่ยงเบนของเรา (สิ่งที่จำเป็นสำหรับการทดสอบเท่านั้น)
npm i --save typescript discord.js inversify dotenv @types/node reflect-metadata npm i --save-dev chai mocha ts-mockito ts-node @types/chai @types/mocha
จากนั้นแทนที่สร้าง 'scripts'
ส่วนใน package.json
กับ:
'scripts': { 'start': 'node src/index.js', 'watch': 'tsc -p tsconfig.json -w', 'test': 'mocha -r ts-node/register 'tests/**/*.spec.ts'' },
เครื่องหมายคำพูดคู่รอบ tests/**/*.spec.ts
จำเป็นในการค้นหาไฟล์แบบวนซ้ำ (หมายเหตุ: ไวยากรณ์อาจแตกต่างกันไปสำหรับนักพัฒนาที่ใช้ Windows)
start
สคริปต์จะถูกใช้เพื่อเริ่มบอทซึ่งเป็น watch
สคริปต์เพื่อคอมไพล์โค้ด TypeScript และ test
เพื่อทำการทดสอบ
ตอนนี้ package.json
ของเรา ไฟล์ควรมีลักษณะดังนี้:
{ 'name': 'typescript-bot', 'version': '1.0.0', 'description': '', 'main': 'index.js', 'dependencies': { '@types/node': '^11.9.4', 'discord.js': '^11.4.2', 'dotenv': '^6.2.0', 'inversify': '^5.0.1', 'reflect-metadata': '^0.1.13', 'typescript': '^3.3.3' }, 'devDependencies': { '@types/chai': '^4.1.7', '@types/mocha': '^5.2.6', 'chai': '^4.2.0', 'mocha': '^5.2.0', 'ts-mockito': '^2.3.1', 'ts-node': '^8.0.3' }, 'scripts': { 'start': 'node src/index.js', 'watch': 'tsc -p tsconfig.json -w', 'test': 'mocha -r ts-node/register 'tests/**/*.spec.ts'' }, 'author': '', 'license': 'ISC' }
ในการโต้ตอบกับ Discord API เราจำเป็นต้องมีโทเค็น ในการสร้างโทเค็นดังกล่าวเราจำเป็นต้องลงทะเบียนแอปใน Discord Developer Dashboard ในการดำเนินการดังกล่าวคุณต้องสร้างบัญชี Discord แล้วไปที่ https://discordapp.com/developers/applications/ . จากนั้นคลิกไฟล์ แอปพลิเคชั่นใหม่ ปุ่ม:
เลือกชื่อและคลิก สร้าง . จากนั้นคลิก บอท → เพิ่มบ็อต และคุณทำเสร็จแล้ว มาเพิ่มบอทลงในเซิร์ฟเวอร์กัน แต่อย่าเพิ่งปิดหน้านี้เราจะต้องคัดลอกโทเค็นเร็ว ๆ นี้
ในการทดสอบบอทของเราเราจำเป็นต้องมีเซิร์ฟเวอร์ Discord คุณสามารถใช้เซิร์ฟเวอร์ที่มีอยู่หรือสร้างใหม่ ในการดำเนินการนี้ให้คัดลอก CLIENT_ID
ของบ็อตที่พบในแท็บข้อมูลทั่วไปและใช้เป็นส่วนหนึ่งของสิ่งนี้ การอนุญาตพิเศษ URL:
https://discordapp.com/oauth2/authorize?client_id=&scope=bot
เมื่อคุณกด URL นี้ในเบราว์เซอร์แบบฟอร์มจะปรากฏขึ้นซึ่งคุณสามารถเลือกเซิร์ฟเวอร์ที่จะเพิ่มบอทได้
หลังจากที่คุณเพิ่มบอทลงในเซิร์ฟเวอร์ของคุณคุณจะเห็นข้อความดังที่กล่าวมา
node.js: javascript ฝั่งเซิร์ฟเวอร์
.env
ไฟล์เราต้องการวิธีบันทึกโทเค็นในแอปของเรา ในการดำเนินการดังกล่าวเราจะใช้ dotenv
แพ็คเกจ ขั้นแรกรับโทเค็นจาก Discord Application Dashboard ( บอท → คลิกเพื่อเปิดเผย Token ):
ตอนนี้สร้าง .env
จากนั้นคัดลอกและวางโทเค็นที่นี่:
TOKEN=paste.the.token.here
หากคุณใช้ Git ไฟล์นี้ควรอยู่ใน .gitignore
เพื่อไม่ให้โทเค็นถูกบุกรุก สร้าง .env.example
เพื่อให้ทราบว่า TOKEN
ต้องการกำหนด:
TOKEN=
ในการคอมไพล์ TypeScript คุณสามารถใช้ npm run watch
คำสั่ง หรืออีกวิธีหนึ่งหากคุณใช้ PHPStorm (หรือ IDE อื่น) เพียงแค่ใช้โปรแกรมดูไฟล์จากปลั๊กอิน TypeScript และปล่อยให้ IDE ของคุณจัดการกับการคอมไพล์ มาทดสอบการตั้งค่าของเราโดยสร้าง src/index.ts
ไฟล์ที่มีเนื้อหา:
console.log('Hello')
เรามาสร้าง tsconfig.json
ไฟล์เหมือนด้านล่าง InversifyJS ต้องการ experimentalDecorators
, emitDecoratorMetadata
, es6
และ reflect-metadata
:
{ 'compilerOptions': { 'module': 'commonjs', 'moduleResolution': 'node', 'target': 'es2016', 'lib': [ 'es6', 'dom' ], 'sourceMap': true, 'types': [ // add node as an option 'node', 'reflect-metadata' ], 'typeRoots': [ // add path to @types 'node_modules/@types' ], 'experimentalDecorators': true, 'emitDecoratorMetadata': true, 'resolveJsonModule': true }, 'exclude': [ 'node_modules' ] }
หากโปรแกรมดูไฟล์ทำงานอย่างถูกต้องควรสร้าง src/index.js
ไฟล์และเรียกใช้ npm start
ควรส่งผลให้:
> node src/index.js Hello
ในที่สุดเรามาเริ่มใช้คุณลักษณะที่มีประโยชน์ที่สุดของ TypeScript: types ไปข้างหน้าและสร้างสิ่งต่อไปนี้ src/bot.ts
ไฟล์:
import {Client, Message} from 'discord.js'; export class Bot { public listen(): Promise { let client = new Client(); client.on('message', (message: Message) => {}); return client.login('token should be here'); } }
ตอนนี้เราสามารถดูสิ่งที่เราต้องการได้ที่นี่โทเค็น! เราจะคัดลอกวางตรงนี้หรือโหลดค่าตรงจากสภาพแวดล้อม?
ทั้งสองอย่าง แต่เรามาเขียนโค้ดที่บำรุงรักษาขยายได้และทดสอบได้มากขึ้นโดยการฉีดโทเค็นโดยใช้ InversifyJS ของเรา
นอกจากนี้เราจะเห็นว่า Client
การพึ่งพาเป็นฮาร์ดโค้ด เรากำลังจะฉีดตัวนี้เหมือนกัน
ถึง ภาชนะฉีดพึ่งพา เป็นวัตถุที่รู้วิธีสร้างอินสแตนซ์วัตถุอื่น ๆ โดยทั่วไปเราจะกำหนดการอ้างอิงสำหรับแต่ละคลาสและคอนเทนเนอร์ DI จะดูแลการแก้ไข
InversifyJS แนะนำให้ใส่การอ้างอิงใน inversify.config.ts
ดังนั้นเรามาเพิ่มคอนเทนเนอร์ DI ของเราที่นั่น:
import 'reflect-metadata'; import {Container} from 'inversify'; import {TYPES} from './types'; import {Bot} from './bot'; import {Client} from 'discord.js'; let container = new Container(); container.bind(TYPES.Bot).to(Bot).inSingletonScope(); container.bind(TYPES.Client).toConstantValue(new Client()); container.bind(TYPES.Token).toConstantValue(process.env.TOKEN); export default container;
นอกจากนี้ เอกสาร InversifyJS แนะนำ การสร้าง types.ts
ไฟล์และรายชื่อแต่ละประเภทที่เราจะใช้ร่วมกับ Symbol
ที่เกี่ยวข้อง สิ่งนี้ค่อนข้างไม่สะดวก แต่ช่วยให้มั่นใจได้ว่าจะไม่มีข้อขัดแย้งในการตั้งชื่อเมื่อแอปของเราเติบโตขึ้น แต่ละ Symbol
เป็นตัวระบุที่ไม่ซ้ำกันแม้ว่าพารามิเตอร์คำอธิบายจะเหมือนกัน (พารามิเตอร์มีไว้เพื่อวัตถุประสงค์ในการดีบักเท่านั้น)
export const TYPES = { Bot: Symbol('Bot'), Client: Symbol('Client'), Token: Symbol('Token'), };
โดยไม่ต้องใช้ Symbol
s นี่คือลักษณะที่เกิดขึ้นเมื่อเกิดความขัดแย้งในการตั้งชื่อ:
Error: Ambiguous match found for serviceIdentifier: MessageResponder Registered bindings: MessageResponder MessageResponder
ณ จุดนี้มันถึงแล้ว มากกว่า ไม่สะดวกในการจัดเรียงที่ MessageResponder
ควรใช้โดยเฉพาะอย่างยิ่งถ้าคอนเทนเนอร์ DI ของเรามีขนาดใหญ่ การใช้ Symbol
s ดูแลเรื่องนี้และเราไม่ได้มีตัวอักษรสตริงแปลก ๆ ในกรณีที่มีสองคลาสที่มีชื่อเดียวกัน
เอกสาร word xml คืออะไร
ตอนนี้เรามาแก้ไข Bot
ของเรา คลาสที่จะใช้คอนเทนเนอร์ เราต้องเพิ่ม @injectable
และ @inject()
คำอธิบายประกอบในการทำเช่นนั้น นี่คือใหม่ Bot
ชั้น:
import {Client, Message} from 'discord.js'; import {inject, injectable} from 'inversify'; import {TYPES} from './types'; import {MessageResponder} from './services/message-responder'; @injectable() export class Bot { private client: Client; private readonly token: string; constructor( @inject(TYPES.Client) client: Client, @inject(TYPES.Token) token: string ) { this.client = client; this.token = token; } public listen(): Promise { this.client.on('message', (message: Message) => { console.log('Message received! Contents: ', message.content); }); return this.client.login(this.token); } }
สุดท้ายมาสร้างอินสแตนซ์บอทของเราใน index.ts
ไฟล์:
require('dotenv').config(); // Recommended way of loading dotenv import container from './inversify.config'; import {TYPES} from './types'; import {Bot} from './bot'; let bot = container.get(TYPES.Bot); bot.listen().then(() => { console.log('Logged in!') }).catch((error) => { console.log('Oh no! ', error) });
ตอนนี้เริ่มบอทและเพิ่มลงในเซิร์ฟเวอร์ของคุณ จากนั้นหากคุณพิมพ์ข้อความในช่องเซิร์ฟเวอร์ข้อความนั้นควรปรากฏในบันทึกบนบรรทัดคำสั่งดังนี้:
> node src/index.js Logged in! Message received! Contents: Test
ในที่สุดเราก็มีการตั้งค่าพื้นฐาน: ประเภท TypeScript และคอนเทนเนอร์การฉีดพึ่งพาภายในบอทของเรา
ตรงไปที่เนื้อหาหลักของบทความนี้นั่นคือการสร้างโค้ดเบสที่สามารถทดสอบได้ ในระยะสั้นโค้ดของเราควรใช้แนวทางปฏิบัติที่ดีที่สุด (เช่น ของแข็ง ) ไม่ซ่อนการอ้างอิงไม่ใช้วิธีการคงที่
นอกจากนี้ ไม่ควรทำให้เกิดผลข้างเคียงเมื่อทำงานและสามารถล้อเลียนได้ง่าย .
เพื่อความเรียบง่ายบอทของเราจะทำเพียงสิ่งเดียว: มันจะค้นหาข้อความที่เข้ามาและหากมีคำว่า 'ping' เราจะใช้คำสั่งบอท Discord ที่มีอยู่คำสั่งหนึ่งเพื่อให้บอทตอบสนองด้วย 'pong! ” ให้กับผู้ใช้นั้น
เพื่อแสดงวิธีการฉีดออบเจ็กต์แบบกำหนดเองลงใน Bot
ออบเจ็กต์และทดสอบหน่วยเราจะสร้างสองคลาส: PingFinder
และ MessageResponder
. เราจะฉีด MessageResponder
ลงใน Bot
ชั้นเรียนและ PingFinder
เป็น MessageResponder
.
นี่คือ src/services/ping-finder.ts
ไฟล์:
import {injectable} from 'inversify'; @injectable() export class PingFinder { private regexp = 'ping'; public isPing(stringToSearch: string): boolean { return stringToSearch.search(this.regexp) >= 0; } }
จากนั้นเราฉีดคลาสนั้นลงใน src/services/message-responder.ts
ไฟล์:
วิธีการเริ่มโครงการ angularjs
import {Message} from 'discord.js'; import {PingFinder} from './ping-finder'; import {inject, injectable} from 'inversify'; import {TYPES} from '../types'; @injectable() export class MessageResponder { private pingFinder: PingFinder; constructor( @inject(TYPES.PingFinder) pingFinder: PingFinder ) { this.pingFinder = pingFinder; } handle(message: Message): Promise { if (this.pingFinder.isPing(message.content)) { return message.reply('pong!'); } return Promise.reject(); } }
สุดท้ายนี่คือการแก้ไข Bot
ซึ่งใช้ MessageResponder
ชั้น:
import {Client, Message} from 'discord.js'; import {inject, injectable} from 'inversify'; import {TYPES} from './types'; import {MessageResponder} from './services/message-responder'; @injectable() export class Bot { private client: Client; private readonly token: string; private messageResponder: MessageResponder; constructor( @inject(TYPES.Client) client: Client, @inject(TYPES.Token) token: string, @inject(TYPES.MessageResponder) messageResponder: MessageResponder) { this.client = client; this.token = token; this.messageResponder = messageResponder; } public listen(): Promise { this.client.on('message', (message: Message) => { if (message.author.bot) { console.log('Ignoring bot message!') return; } console.log('Message received! Contents: ', message.content); this.messageResponder.handle(message).then(() => { console.log('Response sent!'); }).catch(() => { console.log('Response not sent.') }) }); return this.client.login(this.token); } }
ในสถานะนั้นแอปจะไม่สามารถทำงานได้เนื่องจากไม่มีคำจำกัดความสำหรับ MessageResponder
และ PingFinder
ชั้นเรียน มาเพิ่มสิ่งต่อไปนี้ใน inversify.config.ts
ไฟล์:
container.bind(TYPES.MessageResponder).to(MessageResponder).inSingletonScope(); container.bind(TYPES.PingFinder).to(PingFinder).inSingletonScope();
นอกจากนี้เราจะเพิ่มสัญลักษณ์ประเภทให้กับ types.ts
:
MessageResponder: Symbol('MessageResponder'), PingFinder: Symbol('PingFinder'),
ตอนนี้หลังจากรีสตาร์ทแอปบอทควรตอบกลับทุกข้อความที่มี 'ping':
และนี่คือลักษณะที่ปรากฏในบันทึก:
> node src/index.js Logged in! Message received! Contents: some message Response not sent. Message received! Contents: message with ping Ignoring bot message! Response sent!
ตอนนี้เราได้ฉีดการอ้างอิงอย่างถูกต้องแล้วการเขียนการทดสอบหน่วยก็ทำได้ง่าย เราจะใช้ Chai และ ts-mockito สำหรับสิ่งนั้น อย่างไรก็ตามยังมีนักวิ่งทดสอบและห้องสมุดจำลองอื่น ๆ อีกมากมายที่คุณสามารถใช้ได้
ไวยากรณ์การเยาะเย้ยใน ts-mockito ค่อนข้างละเอียด แต่ยังเข้าใจง่าย วิธีตั้งค่า MessageResponder
บริการและฉีด PingFinder
เยาะเย้ยมัน:
let mockedPingFinderClass = mock(PingFinder); let mockedPingFinderInstance = instance(mockedPingFinderClass); let service = new MessageResponder(mockedPingFinderInstance);
ตอนนี้เราได้ตั้งค่าการล้อเลียนแล้วเราสามารถกำหนดผลลัพธ์ของ isPing()
ได้ ควรโทรและยืนยัน reply()
โทร. ประเด็นก็คือในการทดสอบหน่วยเรากำหนดผลลัพธ์ของ isPing()
โทร: true
หรือ false
. ไม่สำคัญว่าเนื้อหาของข้อความจะเป็นอย่างไรดังนั้นในการทดสอบเราใช้ 'Non-empty string'
when(mockedPingFinderClass.isPing('Non-empty string')).thenReturn(true); await service.handle(mockedMessageInstance) verify(mockedMessageClass.reply('pong!')).once();
ชุดทดสอบทั้งหมดมีลักษณะดังนี้:
import 'reflect-metadata'; import 'mocha'; import {expect} from 'chai'; import {PingFinder} from '../../../src/services/ping-finder'; import {MessageResponder} from '../../../src/services/message-responder'; import {instance, mock, verify, when} from 'ts-mockito'; import {Message} from 'discord.js'; describe('MessageResponder', () => { let mockedPingFinderClass: PingFinder; let mockedPingFinderInstance: PingFinder; let mockedMessageClass: Message; let mockedMessageInstance: Message; let service: MessageResponder; beforeEach(() => { mockedPingFinderClass = mock(PingFinder); mockedPingFinderInstance = instance(mockedPingFinderClass); mockedMessageClass = mock(Message); mockedMessageInstance = instance(mockedMessageClass); setMessageContents(); service = new MessageResponder(mockedPingFinderInstance); }) it('should reply', async () => { whenIsPingThenReturn(true); await service.handle(mockedMessageInstance); verify(mockedMessageClass.reply('pong!')).once(); }) it('should not reply', async () => { whenIsPingThenReturn(false); await service.handle(mockedMessageInstance).then(() => { // Successful promise is unexpected, so we fail the test expect.fail('Unexpected promise'); }).catch(() => { // Rejected promise is expected, so nothing happens here }); verify(mockedMessageClass.reply('pong!')).never(); }) function setMessageContents() { mockedMessageInstance.content = 'Non-empty string'; } function whenIsPingThenReturn(result: boolean) { when(mockedPingFinderClass.isPing('Non-empty string')).thenReturn(result); } });
การทดสอบสำหรับ PingFinder
ค่อนข้างเป็นเรื่องเล็กน้อยเนื่องจากไม่มีการอ้างอิงที่จะล้อเลียน นี่คือกรณีทดสอบตัวอย่าง:
describe('PingFinder', () => { let service: PingFinder; beforeEach(() => { service = new PingFinder(); }) it('should find 'ping' in the string', () => { expect(service.isPing('ping')).to.be.true }) });
นอกจากการทดสอบหน่วยแล้วเรายังสามารถเขียนการทดสอบการบูรณาการได้อีกด้วย ความแตกต่างที่สำคัญคือการอ้างอิงในการทดสอบเหล่านั้นไม่ได้ถูกล้อเลียน อย่างไรก็ตามมีการอ้างอิงบางอย่างที่ไม่ควรทดสอบเช่นการเชื่อมต่อ API ภายนอก ในกรณีนี้เราสามารถสร้าง mocks และ rebind
ไปยังภาชนะบรรจุเพื่อให้เครื่องจำลองถูกฉีดเข้าไปแทน ตัวอย่างวิธีการดำเนินการดังต่อไปนี้:
การประเมินฮิวริสติกคืออะไร
import container from '../../inversify.config'; import {TYPES} from '../../src/types'; // ... describe('Bot', () => { let discordMock: Client; let discordInstance: Client; let bot: Bot; beforeEach(() => { discordMock = mock(Client); discordInstance = instance(discordMock); container.rebind(TYPES.Client) .toConstantValue(discordInstance); bot = container.get(TYPES.Bot); }); // Test cases here });
สิ่งนี้นำเราไปสู่ตอนท้ายของบทช่วยสอนบอท Discord ของเรา ขอแสดงความยินดีคุณสร้างมันขึ้นมาอย่างหมดจดโดยมี TypeScript และ DI มาตั้งแต่เริ่มต้น! ตัวอย่างการฉีดการพึ่งพา TypeScript นี้เป็นรูปแบบที่คุณสามารถเพิ่มลงในละครของคุณเพื่อใช้กับโปรเจ็กต์ใด ๆ
นำโลกเชิงวัตถุของ TypeScript ใน JavaScript เป็นการเพิ่มประสิทธิภาพที่ดีเยี่ยมไม่ว่าเราจะทำงานกับโค้ดส่วนหน้าหรือส่วนหลัง เพียงแค่ใช้ประเภทเพียงอย่างเดียวทำให้เราหลีกเลี่ยงจุดบกพร่องต่างๆได้ การมีการฉีดขึ้นต่อกันใน TypeScript จะช่วยผลักดันแนวทางปฏิบัติที่ดีที่สุดในเชิงวัตถุให้มากขึ้นในการพัฒนาที่ใช้ JavaScript
แน่นอนว่าเนื่องจากข้อ จำกัด ของภาษามันจะไม่ง่ายและเป็นธรรมชาติเท่ากับภาษาที่พิมพ์แบบคงที่ แต่สิ่งหนึ่งที่แน่นอนคือ TypeScript การทดสอบหน่วยและการฉีดขึ้นต่อกันทำให้เราสามารถเขียนโค้ดที่อ่านได้ง่ายขึ้นควบคู่กันและดูแลรักษาได้มากขึ้นไม่ว่าเราจะพัฒนาแอปประเภทใดก็ตาม
ที่เกี่ยวข้อง: สร้าง WhatsApp Chatbot ไม่ใช่แอปคุณควรใช้รูปแบบการออกแบบการฉีดแบบพึ่งพาหากคุณต้องการเขียนโค้ดที่สะอาดยิ่งขึ้นในแง่ที่ว่าสามารถทดสอบหน่วยได้บำรุงรักษาได้มากกว่าและทำงานควบคู่กันอย่างหลวม ๆ ด้วยการใช้การฉีดแบบพึ่งพาคุณจะมีสูตรสำหรับรหัสที่สะอาดขึ้นโดยไม่ต้องสร้างล้อใหม่
เราถูกบังคับให้เขียนโค้ดที่ทดสอบหน่วยได้ซึ่งง่ายต่อการบำรุงรักษา การอ้างอิงถูกฉีดผ่านตัวสร้างและสามารถจำลองได้ง่ายในการทดสอบหน่วย นอกจากนี้รูปแบบนี้สนับสนุนให้เราเขียนโค้ดคู่กันอย่างหลวม ๆ
วัตถุประสงค์หลักของ TypeScript คือเพื่อให้โค้ด JavaScript ที่สะอาดและอ่านง่ายขึ้นโดยการเพิ่มประเภท เป็นตัวช่วยสำหรับนักพัฒนาซึ่งส่วนใหญ่มีประโยชน์ใน IDE ภายใต้ฝากระโปรง TypeScript ยังคงถูกแปลงเป็น JavaScript ธรรมดา
บอท Discord เป็นเว็บแอปที่ใช้ Discord API สำหรับการสื่อสาร
บอท Discord สามารถตอบสนองต่อข้อความกำหนดบทบาทตอบสนองด้วยปฏิกิริยาและอื่น ๆ มีเมธอด API สำหรับการกระทำที่ไม่ลงรอยกันที่ผู้ใช้ทั่วไปและผู้ดูแลระบบสามารถทำได้
ประโยชน์หลักของ TypeScript คือช่วยให้นักพัฒนาสามารถกำหนดและใช้ประเภทได้ ด้วยการใช้คำแนะนำประเภททรานสไพเลอร์ (หรือ“ คอมไพเลอร์ต้นทางไปยังแหล่งที่มา”) จะรู้ว่าอ็อบเจ็กต์ชนิดใดควรถูกส่งไปยังเมธอด ตรวจพบข้อผิดพลาดหรือการโทรที่ไม่ถูกต้องในเวลาคอมไพล์ซึ่งทำให้เกิดข้อบกพร่องน้อยลงบนเซิร์ฟเวอร์ที่ใช้งานจริง