ข้ามไปยังเนื้อหา

Declaration Files

declaration file — .d.ts — คือไฟล์ที่มี แต่ type ไม่มี runtime code ใช้อธิบาย shape ของบางอย่างที่อยู่ที่อื่น: JavaScript library, global variable, module นี่คือวิธีที่ TypeScript รู้ type ของ code ที่ไม่ได้ compile เอง

คุณใช้ไฟล์พวกนี้ตลอดโดยไม่รู้ตัว เมื่อคุณ import fs ใน Node แล้วได้ autocomplete นั่นคือ .d.ts เมื่อ document.querySelector มี type นั่นคือ lib.dom.d.ts ตัว type กับ implementation เป็นไฟล์แยกกัน

keyword declare แปลว่า “เชื่อผมสิ ตัวนี้มีอยู่จริงตอน runtime — นี่คือ type ของสิ่งนั้น” keyword นี้แนะนำ ambient declaration: type ที่ไม่มี implementation แนบมา

globals.d.ts
declare const APP_VERSION: string; // injected by the bundler at build time
declare function gtag(...args: any[]): void; // a global from a script tag
// now usable anywhere, fully typed, with no import
console.log(APP_VERSION);
gtag("event", "page_view");

ถ้าไม่มี declare การเขียน const APP_VERSION: string จะเป็น value declaration และ compiler จะคาดหวัง initializer และ emit code ออกมา declare บอกว่า: นี่เป็นแค่คำสัญญาเรื่อง type ล้วน ๆ ไม่ต้อง emit อะไร

สมมติคุณ install JavaScript library เล็ก ๆ ที่ไม่มี type คุณอธิบาย shape ของตัวเองได้ด้วย module declaration:

legacy-lib.d.ts
declare module "legacy-lib" {
export function parse(input: string): { ok: boolean; value: number };
export const version: string;
}

ตอนนี้ import { parse } from "legacy-lib" มี type ครบ แม้ library จะ ship JavaScript ธรรมดามา คุณได้เติม contract ที่ขาดไปด้วยมือ

ส่วนใหญ่มีคนเขียน declaration พวกนั้นให้คุณแล้ว DefinitelyTyped คือ repository ชุมชนขนาดใหญ่ของไฟล์ .d.ts สำหรับ JavaScript library หลายพันตัว publish ภายใต้ scope @types/*:

Terminal window
npm install --save-dev @types/lodash

tsc หยิบ package @types/* จาก node_modules มาใช้อัตโนมัติ การ install @types/lodash จึงทำให้ import _ from "lodash" มี type ทันที library สมัยใหม่หลายตัว ship type ของตัวเองมาใน package (declare ผ่าน field "types" ใน package.json ของตัวเอง) ซึ่งกรณีนั้นคุณไม่ต้องใช้ package @types เลย

ถ้าคุณ publish library ที่เขียนด้วย TypeScript คุณอยาก ship ไฟล์ .d.ts เพื่อให้ผู้ใช้ ของคุณ ได้ type เปิดการ emit สำหรับ declaration:

{ "compilerOptions": { "declaration": true } }

ตอนนี้ tsc ผลิต .d.ts ไว้ข้าง ๆ .js ที่ emit แต่ละไฟล์ แล้วคุณชี้ field "types" ใน package.json ไปที่ entry declaration ผู้ใช้ของคุณ import library แล้วได้ type information ครบ — กลไกเดียวกับที่ทั้ง ecosystem ใช้

ไฟล์ `.d.ts` มีอะไรอยู่ข้างใน?
keyword `declare` ทำอะไร?
`@types/lodash` และ package คล้าย ๆ กันมาจากไหน?
จะ ship type อย่างไรเมื่อ publish TypeScript library ของตัวเอง?