TypeScript และ StorageManager

TypeScript และ StorageManager

บทความนี้อธิบายเกี่ยวกับ TypeScript และ StorageManager

เรามีตัวอย่างการใช้งานจริงเพื่ออธิบายเกี่ยวกับ TypeScript และ StorageManager

YouTube Video

TypeScript และ StorageManager

StorageManager คืออะไร?

StorageManager เป็น API ที่ช่วยให้เว็บแอปพลิเคชันสามารถประเมินการใช้งานพื้นที่จัดเก็บและความจุที่ยังเหลืออยู่ได้ นอกจากนี้ ยังมีฟังก์ชัน persist (persist()) ที่ช่วยป้องกันไม่ให้ข้อมูลถูกลบโดยอัตโนมัติจากผู้ใช้หรือเบราว์เซอร์ คุณจึงสามารถควบคุมได้ว่ามีพื้นที่ว่างเพียงพอหรือไม่ หรือข้อมูลจะถูกลบโดยอัตโนมัติ เช่น การเก็บข้อมูลจำนวนมากใน IndexedDB สำหรับแอปพลิเคชันออฟไลน์

การตรวจจับฟีเจอร์

ขั้นแรก ตรวจสอบว่าเบราว์เซอร์รองรับ navigator.storage หรือไม่

 1// TypeScript: feature detection for StorageManager
 2function supportsStorageManager(): boolean {
 3  return typeof navigator !== "undefined" &&
 4         typeof navigator.storage !== "undefined" &&
 5         typeof navigator.storage.estimate === "function";
 6}
 7
 8// Example usage
 9if (supportsStorageManager()) {
10  console.log("StorageManager is supported.");
11} else {
12  console.warn("StorageManager not supported. Falling back to localStorage or IndexedDB.");
13}
  • ฟังก์ชันนี้จะตรวจสอบการใช้งานของ StorageManager API เป็นลำดับขั้น
  • เนื่องจาก navigator.storage ไม่มีอยู่ในบางเบราว์เซอร์เช่น Safari จึงควรตรวจสอบอย่างปลอดภัยด้วย typeof

การประเมินการใช้พื้นที่จัดเก็บ (estimate())

หลังจากตรวจสอบความสามารถในการใช้งานด้วย supportsStorageManager() ให้ดึงข้อมูลการใช้งาน (usage) และโควตา (quota) จาก navigator.storage.estimate()

 1// TypeScript: safely get storage estimate
 2async function getStorageEstimate(): Promise<{ usage: number; quota: number } | null> {
 3  if (!supportsStorageManager()) {
 4    console.warn("StorageManager not supported.");
 5    return null;
 6  }
 7
 8  const estimate = await navigator.storage.estimate();
 9  return { usage: estimate.usage ?? 0, quota: estimate.quota ?? 0 };
10}
11
12// Example usage
13getStorageEstimate().then(result => {
14  if (result) {
15    console.log(`Usage: ${result.usage} bytes / Quota: ${result.quota} bytes`);
16  }
17});
  • ฟังก์ชันนี้ทำงานอย่างปลอดภัยเสมอ และจะคืนค่าเป็น null หากเบราว์เซอร์ไม่รองรับ
  • usage และ quota เป็นค่าประมาณการณ์และอาจแตกต่างกันไปในแต่ละเบราว์เซอร์

การร้องขอให้ข้อมูลคงอยู่ (persist())

ใช้ persist() เพื่อขอให้ข้อมูลสำคัญ (เช่น cache สำหรับออฟไลน์) ไม่ถูกลบโดยอัตโนมัติจากเบราว์เซอร์ แต่อาจไม่สำเร็จในทุกสภาพแวดล้อม

 1// TypeScript: safely request persistent storage
 2async function requestPersistence(): Promise<boolean> {
 3  if (!supportsStorageManager()) {
 4    console.warn("StorageManager not supported.");
 5    return false;
 6  }
 7
 8  if (typeof navigator.storage.persist !== "function") {
 9    console.warn("persist() not available in this browser.");
10    return false;
11  }
12
13  try {
14    const granted = await navigator.storage.persist();
15    return Boolean(granted);
16  } catch (err) {
17    console.error("persist() error:", err);
18    return false;
19  }
20}
21
22// Example usage
23requestPersistence().then(granted => {
24  console.log("Persistence granted?", granted);
25});
  • หาก persist() สำเร็จ ข้อมูลจะไม่ถูกลบโดยอัตโนมัติจากเบราว์เซอร์ ยกเว้นผู้ใช้จะลบเอง อย่างไรก็ตาม คำขออาจถูกปฏิเสธขึ้นอยู่กับการกระทำของผู้ใช้หรือการตั้งค่าเบราว์เซอร์

ตรวจสอบพื้นที่ว่างก่อนบันทึกข้อมูล

ตรวจสอบพื้นที่ว่างก่อนบันทึกข้อมูลขนาดใหญ่เพื่อป้องกันข้อผิดพลาดขณะเขียนข้อมูล (QuotaExceededError)

 1// TypeScript: ensure enough space before writing
 2async function ensureSpaceAndWrite(neededBytes: number, writeFn: () => Promise<void>): Promise<boolean> {
 3  const estimate = await getStorageEstimate();
 4  if (!estimate) {
 5    console.warn("Cannot check storage space. Proceeding without validation.");
 6    await writeFn();
 7    return true;
 8  }
 9
10  const free = estimate.quota - estimate.usage;
11  if (free < neededBytes) {
12    console.warn(`Not enough space. Free: ${free} bytes, needed: ${neededBytes} bytes.`);
13    return false;
14  }
15
16  await writeFn();
17  return true;
18}
19
20// Example usage
21ensureSpaceAndWrite(10 * 1024 * 1024, async () => {
22  console.log("Saving large data...");
23});
  • การตรวจสอบพื้นที่ว่างก่อนบันทึก ช่วยลดความเสี่ยงจากการเขียนข้อมูลล้มเหลวเนื่องจากพื้นที่ไม่เพียงพอ

การจัดการ localStorage แบบ type-safe ใน TypeScript

localStorage เหมาะกับการเก็บข้อมูลการตั้งค่าขนาดเล็กและข้อมูลที่คล้ายกัน

คลาสด้านล่างนี้ใช้ generics เพื่อสร้าง wrapper ที่ type-safe

 1// TypeScript: typed localStorage wrapper
 2type Serializer<T> = {
 3  serialize: (v: T) => string;
 4  deserialize: (s: string) => T;
 5};
 6
 7class TypedLocalStorage<K extends string, V> {
 8  constructor(private storage: Storage, private serializer: Serializer<V>) {}
 9
10  set(key: K, value: V): void {
11    this.storage.setItem(key, this.serializer.serialize(value));
12  }
13
14  get(key: K): V | null {
15    const raw = this.storage.getItem(key);
16    if (raw === null) return null;
17    try {
18      return this.serializer.deserialize(raw);
19    } catch {
20      return null;
21    }
22  }
23
24  remove(key: K): void {
25    this.storage.removeItem(key);
26  }
27
28  clear(): void {
29    this.storage.clear();
30  }
31}
32
33// Example usage
34const jsonSerializer: Serializer<any> = {
35  serialize: v => JSON.stringify(v),
36  deserialize: s => JSON.parse(s),
37};
38
39const appStorage = new TypedLocalStorage<'theme' | 'token', any>(localStorage, jsonSerializer);
40appStorage.set('theme', { dark: true });
41console.log(appStorage.get('theme'));
  • คลาสนี้ช่วยลดความเสี่ยงจากการเก็บหรือเรียกข้อมูลผิดประเภท

ข้อควรพิจารณาและแนวปฏิบัติที่ดี

  • ควรตรวจสอบว่าเบราว์เซอร์รองรับ StorageManager อยู่เสมอ เนื่องจาก StorageManager เป็น API ใหม่เสมอ ควรตรวจสอบการมีอยู่ก่อนใช้งานทุกครั้ง

  • ควรใส่ใจกับข้อจำกัดและความเข้ากันได้ของพื้นที่จัดเก็บ ค่าที่ได้จาก estimate() แตกต่างกันไปในแต่ละเบราว์เซอร์และเป็นเพียงค่าประมาณการณ์

  • ออกแบบโดยคำนึงถึงการทำงานแบบอะซิงโครนัสของ API estimate() และ persist() เป็น API แบบอะซิงโครนัสที่ใช้ Promise เขียนโค้ดให้ UI ไม่ถูกบล็อกจากการทำงานของ API เหล่านี้

  • เรื่องความปลอดภัย ไม่ควรเก็บข้อมูลสำคัญ เช่น access token ไว้ใน localStorage ควรใช้ HttpOnly cookies หรือการเข้ารหัสแทน หากเป็นไปได้

  • การออกแบบ fallback หาก supportsStorageManager() คืนค่า false ควรเพิ่มตรรกะเพื่อเปลี่ยนไปใช้เพียง localStorage หรือ IndexedDB เท่านั้น

สรุป

ด้วยการตรวจจับฟีเจอร์ด้วย supportsStorageManager() คุณสามารถรองรับทุกสภาพแวดล้อมได้อย่างปลอดภัย พร้อมทั้งตรวจสอบการใช้งานและโควต้าผ่าน navigator.storage.estimate() และร้องขอการคงอยู่ของข้อมูลด้วย persist() ใน TypeScript ควรใช้ฟังก์ชัน wrapper และกำหนด type เพื่อให้โค้ดอ่านง่ายและปลอดภัย โดยลำดับพื้นฐานคือ: ตรวจสอบการมีอยู่, ประเมิน, ร้องขอการคงอยู่, และตรวจสอบความจุก่อนบันทึกข้อมูล

คุณสามารถติดตามบทความข้างต้นโดยใช้ Visual Studio Code บนช่อง YouTube ของเรา กรุณาตรวจสอบช่อง YouTube ด้วย

YouTube Video