TypeScript और IndexedDB

यह लेख TypeScript और IndexedDB के बारे में समझाता है।

हम वास्तविक उदाहरणों के साथ TypeScript और IndexedDB समझाएंगे।

YouTube Video

TypeScript और IndexedDB

IndexedDB एक लो-लेवल NoSQL स्टोरेज है जो आपको ब्राउज़र में संरचित डेटा रखने की अनुमति देता है। TypeScript के साथ, आप स्कीमाओं को टाइप-सुरक्षित तरीक़े से दर्शा सकते हैं, जिससे त्रुटियाँ कम होती हैं और मेंटेनेबिलिटी बढ़ती है।

मूल शब्दावली और कार्यप्रवाह

IndexedDB ब्राउज़र के भीतर एक छोटी डेटाबेस है। यह नाम और संस्करण वाली डेटाबेस, ऑब्जेक्ट स्टोर्स, लेन-देन, इंडेक्स और कर्सर जैसी प्रणालियों के माध्यम से डेटा को प्रबंधित करता है। डेटाबेस के संस्करण होते हैं, और जब संस्करण उन्नत किया जाता है, तो स्कीमा को अपडेट करने के लिए onupgradeneeded को कॉल किया जाता है, जैसे कि तालिकाएं बनाना या संशोधित करना।

IndexedDB खोलना (मूल पैटर्न)

पहले, हम IndexedDB के साथ डेटाबेस खोलने और यदि आवश्यक हो तो onupgradeneeded में एक ऑब्जेक्ट स्टोर बनाने का उदाहरण दिखाते हैं।

 1// Open an IndexedDB database and create an object store if needed.
 2// This code shows the classic callback-based IndexedDB API wrapped into a Promise.
 3function openDatabase(dbName: string, version: number): Promise<IDBDatabase> {
 4  return new Promise((resolve, reject) => {
 5    const request = indexedDB.open(dbName, version);
 6
 7    request.onerror = () => {
 8      reject(request.error);
 9    };
10
11    request.onupgradeneeded = (event) => {
12      const db = request.result;
13      if (!db.objectStoreNames.contains('todos')) {
14        // Create object store with keyPath 'id'
15        db.createObjectStore('todos', { keyPath: 'id' });
16      }
17    };
18
19    request.onsuccess = () => {
20      resolve(request.result);
21    };
22  });
23}
24
25// Usage example:
26openDatabase('my-db', 1)
27  .then(db => {
28    console.log('DB opened', db.name, db.version);
29    db.close();
30  })
31  .catch(err => console.error('Failed to open DB', err));
  • यह कोड डेटाबेस को खोलता है और लॉग करता है कि यह सफल रहा या विफल।
  • यदि ज़रूरत हो, तो todos स्टोर onupgradeneeded में बनाया जाता है।

TypeScript में प्रकार परिभाषित करना (मॉडल्स)

इसके बाद, हम TypeScript का उपयोग करके डेटा प्रकार परिभाषित करते हैं। यह बाद के CRUD ऑपरेशनों में टाइप सेफ्टी सुनिश्चित करता है।

1// Define the TypeScript interface for a Todo item.
2// This helps with type safety in the database operations.
3interface Todo {
4  id: string;          // primary key
5  title: string;
6  completed: boolean;
7  createdAt: number;
8}
  • यहाँ, हम Todo प्रकार परिभाषित करते हैं।

सरल CRUD फ़ंक्शन कार्यान्वयन उदाहरण

इसके बाद, हम ऑब्जेक्ट स्टोर से जोड़ने, प्राप्त करने, अपडेट और डिलीट करने जैसे मूल CRUD ऑपरेशनों को दिखाते हैं। हर फ़ंक्शन एक IDBDatabase लेता है और एक Promise लौटाता है।

 1// CRUD utilities for the 'todos' object store.
 2// Each operation uses a transaction and returns a Promise for easier async/await usage.
 3
 4function addTodo(db: IDBDatabase, todo: Todo): Promise<void> {
 5  return new Promise((resolve, reject) => {
 6    const tx = db.transaction('todos', 'readwrite');
 7    const store = tx.objectStore('todos');
 8    const req = store.add(todo);
 9
10    req.onsuccess = () => {
11      console.log('Todo added', todo.id);
12    };
13    req.onerror = () => reject(req.error);
14
15    tx.oncomplete = () => resolve();
16    tx.onerror = () => reject(tx.error);
17  });
18}
  • यह फ़ंक्शन IndexedDB के todos स्टोर में एक नया Todo जोड़ता है। यह असिंक्रोनस हैंडलिंग के लिए एक Promise लौटाता है, जो प्रक्रिया पूरी होने पर हल हो जाता है।
 1function getTodo(db: IDBDatabase, id: string): Promise<Todo | undefined> {
 2  return new Promise((resolve, reject) => {
 3    const tx = db.transaction('todos', 'readonly');
 4    const store = tx.objectStore('todos');
 5    const req = store.get(id);
 6
 7    req.onsuccess = () => resolve(req.result as Todo | undefined);
 8    req.onerror = () => reject(req.error);
 9  });
10}
  • यह फ़ंक्शन निर्दिष्ट आईडी के साथ Todo को पुनः प्राप्त करता है और यदि ऑब्जेक्ट मिलता है तो उसे लौटाता है। अगर कोई मेल खाता डेटा नहीं मिलता, तो यह undefined लौटाता है।
 1function updateTodo(db: IDBDatabase, todo: Todo): Promise<void> {
 2  return new Promise((resolve, reject) => {
 3    const tx = db.transaction('todos', 'readwrite');
 4    const store = tx.objectStore('todos');
 5    const req = store.put(todo);
 6
 7    req.onsuccess = () => {
 8      console.log('Todo updated', todo.id);
 9    };
10    req.onerror = () => reject(req.error);
11
12    tx.oncomplete = () => resolve();
13    tx.onerror = () => reject(tx.error);
14  });
15}
  • यह फ़ंक्शन मौजूदा Todo डेटा को अपडेट करता है। सफल होने पर, अपडेट किए गए Todo की आईडी लॉग की जाती है।
 1function deleteTodo(db: IDBDatabase, id: string): Promise<void> {
 2  return new Promise((resolve, reject) => {
 3    const tx = db.transaction('todos', 'readwrite');
 4    const store = tx.objectStore('todos');
 5    const req = store.delete(id);
 6
 7    req.onsuccess = () => {
 8      console.log('Todo deleted', id);
 9    };
10    req.onerror = () => reject(req.error);
11
12    tx.oncomplete = () => resolve();
13    tx.onerror = () => reject(tx.error);
14  });
15}
  • यह फ़ंक्शन निर्दिष्ट आईडी वाले Todo को हटाता है। प्रक्रिया सफल होने पर, हटाई गई ID लॉग की जाती है।

  • ये फ़ंक्शन्स लेन-देन के पूर्ण होने या त्रुटियों के आधार पर एक Promise को पूर्ण या अस्वीकृत करते हैं। console.log के माध्यम से आउटपुट शामिल करना निष्पादन के दौरान क्या हो रहा है, इसे ट्रैक करना आसान बनाता है।

इंडेक्स और सम्मिलित क्वेरीज़

IndexedDB में इंडेक्स का उपयोग करके, आप विशिष्ट फ़ील्ड्स पर कुशलता से खोज सकते हैं। यहाँ, हम createdAt के लिए एक इंडेक्स बनाते हैं और रेंज क्वेरी का उदाहरण देते हैं।

 1// When opening DB, create an index for createdAt.
 2// Then demonstrate a range query using the index.
 3
 4function openDatabaseWithIndex(dbName: string, version: number): Promise<IDBDatabase> {
 5  return new Promise((resolve, reject) => {
 6    const request = indexedDB.open(dbName, version);
 7
 8    request.onupgradeneeded = () => {
 9      const db = request.result;
10      if (!db.objectStoreNames.contains('todos')) {
11        const store = db.createObjectStore('todos', { keyPath: 'id' });
12        // Create an index on createdAt for sorting/filtering
13        store.createIndex('by-createdAt', 'createdAt', { unique: false });
14      } else {
15        const store = request.transaction!.objectStore('todos');
16        if (!store.indexNames.contains('by-createdAt')) {
17          store.createIndex('by-createdAt', 'createdAt', { unique: false });
18        }
19      }
20    };
21
22    request.onerror = () => reject(request.error);
23    request.onsuccess = () => resolve(request.result);
24  });
25}
  • यह फ़ंक्शन डेटाबेस खोलता है और createdAt फ़ील्ड पर by-createdAt इंडेक्स बनाता या सत्यापित करता है। यह निर्माण तिथि के आधार पर कुशल खोज और छंटाई की अनुमति देता है।
 1async function getTodosCreatedAfter(db: IDBDatabase, timestamp: number): Promise<Todo[]> {
 2  return new Promise((resolve, reject) => {
 3    const tx = db.transaction('todos', 'readonly');
 4    const store = tx.objectStore('todos');
 5    const index = store.index('by-createdAt');
 6    const range = IDBKeyRange.lowerBound(timestamp, true); // exclusive
 7    const req = index.openCursor(range);
 8
 9    const results: Todo[] = [];
10    req.onsuccess = (event) => {
11      const cursor = (event.target as IDBRequest).result as IDBCursorWithValue | null;
12      if (cursor) {
13        results.push(cursor.value as Todo);
14        cursor.continue();
15      } else {
16        resolve(results);
17      }
18    };
19    req.onerror = () => reject(req.error);
20  });
21}
  • यह फ़ंक्शन केवल वही Todos वापस लाता है जो निर्दिष्ट टाइमस्टैम्प के बाद बनाए गए हैं। इंडेक्स का उपयोग करने से निर्माण तिथि क्रम में कुशल डेटा स्कैनिंग संभव होती है।

  • इस उदाहरण में, डेटाबेस अपडेट के समय एक by-createdAt इंडेक्स बनाया जाता है, और निर्दिष्ट समय के बाद बनाए गए Todo आइटम्स को कर्सर के साथ गिनती की जाती है।

Promise आधारित हल्का रैपर

लो-लेवल IndexedDB API लिखना जटिल है, और एक जैसे ऑपरेशन्स को बार-बार करने से अनावश्यकता और बग्स आ सकते हैं। इसलिए, यदि हम ऑपरेशनों को सारगर्भित करने वाला एक सामान्य TypeScript रैपर क्लास तैयार करते हैं, तो कोड को सरल और अनुरक्षणीय बनाना आसान हो जाता है। नीचे एक कार्यान्वयन है जो मूल कार्यक्षमता पर केंद्रित है।

1// A minimal TypeScript wrapper around IndexedDB to simplify common operations.
2// This class is generic over the store's value type and assumes 'keyPath' is 'id'.
3
4class IDBWrapper<T extends { id: string }> {
5  private dbPromise: Promise<IDBDatabase>;
6
7  constructor(private dbName: string, private version: number, private storeName: string) {
8    this.dbPromise = this.open();
9  }
  • यह क्लास IndexedDB संचालन को रैप करती है और टाइप-सुरक्षित CRUD विधियां प्रदान करती है। यह मानता है कि ऑब्जेक्ट स्टोर में कुंजी id है।
 1  private open(): Promise<IDBDatabase> {
 2    return new Promise((resolve, reject) => {
 3      const req = indexedDB.open(this.dbName, this.version);
 4      req.onerror = () => reject(req.error);
 5      req.onupgradeneeded = () => {
 6        const db = req.result;
 7        if (!db.objectStoreNames.contains(this.storeName)) {
 8          db.createObjectStore(this.storeName, { keyPath: 'id' });
 9        }
10      };
11      req.onsuccess = () => resolve(req.result);
12    });
13  }
  • यह डेटाबेस खोलता है और आवश्यक होने पर एक नया ऑब्जेक्ट स्टोर बनाता है। स्टोर का इनिशियलाइज़ेशन अपग्रेड इवेंट का उपयोग करके किया जाता है।
 1  async add(item: T): Promise<void> {
 2    const db = await this.dbPromise;
 3    await new Promise<void>((resolve, reject) => {
 4      const tx = db.transaction(this.storeName, 'readwrite');
 5      const store = tx.objectStore(this.storeName);
 6      const req = store.add(item);
 7      req.onsuccess = () => {
 8        console.log('added', item.id);
 9      };
10      req.onerror = () => reject(req.error);
11      tx.oncomplete = () => resolve();
12      tx.onerror = () => reject(tx.error);
13    });
14  }
  • IndexedDB स्टोर में डेटा जोड़ता है। जोड़ने के बाद, ID को कंसोल में लॉग किया जाता है।
 1  async get(id: string): Promise<T | undefined> {
 2    const db = await this.dbPromise;
 3    return new Promise((resolve, reject) => {
 4      const tx = db.transaction(this.storeName, 'readonly');
 5      const store = tx.objectStore(this.storeName);
 6      const req = store.get(id);
 7      req.onsuccess = () => resolve(req.result as T | undefined);
 8      req.onerror = () => reject(req.error);
 9    });
10  }
  • निर्दिष्ट ID के अनुरूप डेटा प्राप्त करता है। यदि डेटा मौजूद नहीं है, तो undefined लौटाया जाता है।
 1  async put(item: T): Promise<void> {
 2    const db = await this.dbPromise;
 3    return new Promise((resolve, reject) => {
 4      const tx = db.transaction(this.storeName, 'readwrite');
 5      const store = tx.objectStore(this.storeName);
 6      const req = store.put(item);
 7      req.onsuccess = () => {
 8        console.log('put', item.id);
 9      };
10      req.onerror = () => reject(req.error);
11      tx.oncomplete = () => resolve();
12      tx.onerror = () => reject(tx.error);
13    });
14  }
  • मौजूदा डेटा को अपडेट करता है या नया डेटा जोड़ता है। प्रसंस्करण के बाद, अपडेटेड ID लॉग होती है।
 1  async delete(id: string): Promise<void> {
 2    const db = await this.dbPromise;
 3    return new Promise((resolve, reject) => {
 4      const tx = db.transaction(this.storeName, 'readwrite');
 5      const store = tx.objectStore(this.storeName);
 6      const req = store.delete(id);
 7      req.onsuccess = () => {
 8        console.log('deleted', id);
 9      };
10      req.onerror = () => reject(req.error);
11      tx.oncomplete = () => resolve();
12      tx.onerror = () => reject(tx.error);
13    });
14  }
  • निर्दिष्ट ID वाला डेटा हटाता है। सफल होने पर, हटाई गई ID लॉग होती है।
 1  async getAll(): Promise<T[]> {
 2    const db = await this.dbPromise;
 3    return new Promise((resolve, reject) => {
 4      const tx = db.transaction(this.storeName, 'readonly');
 5      const store = tx.objectStore(this.storeName);
 6      const req = store.getAll();
 7      req.onsuccess = () => resolve(req.result as T[]);
 8      req.onerror = () => reject(req.error);
 9    });
10  }
11}
  • स्टोर में सभी डेटा प्राप्त करता है। लौटने वाला मान T प्रकार की एक एरे है।
 1// Example usage with Todo type:
 2interface Todo {
 3  id: string;
 4  title: string;
 5  completed: boolean;
 6  createdAt: number;
 7}
 8
 9const todoStore = new IDBWrapper<Todo>('my-db', 1, 'todos');
10
11(async () => {
12  const newTodo: Todo = { id: '1', title: 'Learn IndexedDB', completed: false, createdAt: Date.now() };
13  await todoStore.add(newTodo);
14  const fetched = await todoStore.get('1');
15  console.log('fetched', fetched);
16  newTodo.completed = true;
17  await todoStore.put(newTodo);
18  const all = await todoStore.getAll();
19  console.log('all todos', all);
20  await todoStore.delete('1');
21})();
  • यह कोड IDBWrapper क्लास के वास्तविक उपयोग का उदाहरण है। यह Todo डेटा जोड़ने, प्राप्त करने, अपडेट करने, सूचीबद्ध करने और हटाने की प्रक्रिया को दर्शाता है।

  • यह रैपर बेसिक CRUD ऑपरेशनों को सरलता से संभालने की अनुमति देता है। वास्तविक दुनिया के वातावरण में, आपको त्रुटि प्रबंधन और स्कीमा प्रबंधन (इंडेक्स) को भी संभालना होता है।

स्कीमा माइग्रेशन (संस्करण उन्नयन)

डेटाबेस स्कीमा बदलने के लिए, indexedDB.open के दूसरे आर्ग्युमेंट (संस्करण) को बढ़ाएँ और उसे onupgradeneeded में अपडेट करें। आपको इसे इस तरह डिज़ाइन करने की आवश्यकता है कि मौजूदा ट्रांज़ेक्शन पूरे हो जाएँ और विनाशकारी परिवर्तन से बचा जा सके।

 1// Example of handling upgrade to version 2: add an index and perhaps migrate data.
 2// onupgradeneeded receives an event where oldVersion and newVersion are accessible.
 3
 4function upgradeToV2(dbName: string): Promise<IDBDatabase> {
 5  return new Promise((resolve, reject) => {
 6    const req = indexedDB.open(dbName, 2);
 7    req.onupgradeneeded = (ev) => {
 8      const db = req.result;
 9      const oldVersion = (ev as IDBVersionChangeEvent).oldVersion;
10      console.log('Upgrading DB from', oldVersion, 'to', db.version);
11      let store: IDBObjectStore;
12      if (!db.objectStoreNames.contains('todos')) {
13        store = db.createObjectStore('todos', { keyPath: 'id' });
14      } else {
15        store = req.transaction!.objectStore('todos');
16      }
17      // Add index if not present
18      if (!store.indexNames.contains('by-completed')) {
19        store.createIndex('by-completed', 'completed', { unique: false });
20      }
21
22      // Optional: data migration logic if necessary can go here,
23      // but heavy migrations often should be done lazily on read.
24    };
25    req.onsuccess = () => resolve(req.result);
26    req.onerror = () => reject(req.error);
27  });
28}
  • onupgradeneeded के अंदर भारी प्रोसेसिंग UI को ब्लॉक कर सकती है, इसलिए इसे न्यूनतम रखें, और यदि संभव हो तो विलंबित माइग्रेशन (ऐप स्टार्टअप के दौरान चरणबद्ध प्रोसेसिंग) पर विचार करें।

लेन-देन के बारे में सावधानियाँ (कार्यावधि और त्रुटियाँ)

लेन-देन को बनाने वाली स्क्रिप्ट के निष्पादन के समाप्त होने से पहले वे स्वतः ही समर्पित हो जाते हैं। लेन-देन के भीतर await का उपयोग करते समय, वह अनपेक्षित रूप से कमिट हो सकता है; एक ही लेन-देन के भीतर अनेक असिंक ऑपरेशनों के लिए सावधानी आवश्यक है।

 1// Bad pattern: awaiting outside transaction callbacks can cause tx to auto-commit.
 2// Good pattern is to chain requests and resolve on tx.oncomplete as shown earlier.
 3
 4// Example: Do multiple operations inside single tx, avoid awaiting inside.
 5function multiOperation(db: IDBDatabase, items: Todo[]): Promise<void> {
 6  return new Promise((resolve, reject) => {
 7    const tx = db.transaction('todos', 'readwrite');
 8    const store = tx.objectStore('todos');
 9
10    for (const item of items) {
11      const req = store.put(item);
12      req.onerror = () => console.error('put error', req.error);
13      // Do NOT await here; just schedule requests synchronously.
14    }
15
16    tx.oncomplete = () => {
17      console.log('All operations in transaction completed');
18      resolve();
19    };
20    tx.onerror = () => reject(tx.error);
21  });
22}
  • लेन-देन की अवधि को ध्यान में रखें; यदि आवश्यकता हो तो अलग लेन-देन का उपयोग करें, या एक लेन-देन के भीतर ऑपरेशन सिंक्रोनस रूप से शेड्यूल करें।

कर्सर अनुप्रयोग और पेजिनेशन

कर्सर का उपयोग करके, आप बड़े पैमाने पर डेटा को क्रमवार प्रोसेस कर सकते हैं या ऑफ़सेट्स का उपयोग किए बिना सरल पेजीनशन लागू कर सकते हैं।

 1// Example: fetch first N items using a cursor (ascending by key).
 2function fetchFirstN(db: IDBDatabase, n: number): Promise<Todo[]> {
 3  return new Promise((resolve, reject) => {
 4    const tx = db.transaction('todos', 'readonly');
 5    const store = tx.objectStore('todos');
 6    const req = store.openCursor();
 7    const out: Todo[] = [];
 8    req.onsuccess = (ev) => {
 9      const cursor = (ev.target as IDBRequest).result as IDBCursorWithValue | null;
10      if (cursor && out.length < n) {
11        out.push(cursor.value as Todo);
12        cursor.continue();
13      } else {
14        resolve(out);
15      }
16    };
17    req.onerror = () => reject(req.error);
18  });
19}
  • कर्सर के जरिए एक-एक आइटम लाने से मेमोरी का उपयोग कम किया जा सकता है। पेजिंग लागू करते समय आखिरी पढ़ी गई कुंजी को याद रखना आम है।

त्रुटि प्रबंधन और विकल्प

1// Feature detection
2if (!('indexedDB' in window)) {
3  console.warn('IndexedDB is not supported. Falling back to localStorage.');
4  // implement fallback logic...
5}
  • ब्राउज़र के बीच कार्यान्वयन में अंतर या उपयोगकर्ताओं की गोपनीयता सेटिंग्स (जैसे प्राइवेट ब्राउज़िंग) के कारण IndexedDB उपलब्ध नहीं हो सकता है। indexedDB मौजूद है या नहीं, इसकी जांच करें और यदि नहीं है, तो localStorage जैसी बैकअप व्यवस्था प्रदान करें।

प्रदर्शन और सर्वोत्तम प्रथाएँ

IndexedDB तेज़ और शक्तिशाली है, लेकिन इसकी डिज़ाइन और डेटा की हैंडलिंग पर प्रदर्शन बहुत भिन्न हो सकता है। उपयोग केस के अनुसार, अनुकूलन निम्न तरीकों से किया जा सकता है:।

  • ऑब्जेक्ट स्टोर को वास्तविक उपयोग के अनुसार डिज़ाइन करें। उदाहरण के लिए, यदि पढ़ने की क्रियाएं अधिक हैं तो इंडेक्स दें; यदि लिखने की क्रियाएं अधिक हैं तो की डिज़ाइन सरल रखें।
  • बड़े द्विआधारी डेटा जैसे छवियाँ और ऑडियो ब्लॉब्स के रूप में संग्रहित किए जाने चाहिए, या आवश्यकता होने पर उन्हें File API या सेवा श्रमिकों के माध्यम से प्रबंधित किया जाना चाहिए। आवश्यकता पड़ने पर संपीड़न पर भी विचार किया जा सकता है।
  • लेन-देन को जितना संभव हो उतना संक्षिप्त रखें और भारी प्रसंस्करण को लेन-देन के बाहर करें ताकि लॉक समय कम से कम किया जा सके।
  • इंडेक्स खोज को तेज़ कर सकते हैं, लेकिन डाले जाने और अद्यतन करने की प्रक्रिया को धीमा कर सकते हैं, इसलिए केवल वे ही बनाएँ जो वास्तव में आवश्यक हों।
  • जब कई छोटे डेटा भागों के साथ काम किया जाता है, तो उन्हें एक साथ getAll() का उपयोग करके प्राप्त करना मेमोरी को समाप्त कर सकता है। कर्सर के माध्यम से प्रोसेसिंग को विभाजित करके आप मेमोरी उपयोग को कम कर सकते हैं।

सुरक्षा और गोपनीयता

IndexedDB डेटा, समान ओरिजिन नीति के अनुसार, प्रति डोमेन और प्रोटोकॉल पृथक होता है। यह मानकर डिजाइन करें कि यदि उपयोगकर्ता ब्राउज़र डेटा हटाते हैं या निजी मोड का उपयोग करते हैं तो डेटा खो सकता है।

सारांश और अनुशंसित डिज़ाइन पैटर्न

IndexedDB को TypeScript के साथ प्रभावी रूप से उपयोग करने के लिए प्रकारों और असिंक्रोनस प्रोसेस का प्रबंध करना, संस्करण प्रबंधन और लेन-देन डिजाइन के प्रति सचेत रहना, और सामान्य प्रोसेस को लपेटना अनिवार्य है ताकि अनुरक्षण क्षमता बढ़ सके।

  • TypeScript में टाइप परिभाषित करना और IndexedDB ऑपरेशन को Promise/async/await से रैप करना सुरक्षा और कोड पठनीयता में सुधार करता है।
  • स्कीमा परिवर्तन के लिए onupgradeneeded के साथ संस्करण प्रबंधन का उपयोग करें, और भारी प्रोसेसिंग को संभव हो तो बाद में करें।
  • लेन-देन को छोटा डिज़ाइन करें और एक ही लेन-देन में भारी असिंक प्रोसेसिंग से बचें।
  • रैपर क्लासेस बनाकर, आप त्रुटि प्रबंधन, लॉगिंग और प्रकार परिभाषाओं जैसी अनावश्यक सामान्य प्रक्रियाओं को कम कर सकते हैं।

आप हमारे YouTube चैनल पर Visual Studio Code का उपयोग करके ऊपर दिए गए लेख के साथ आगे बढ़ सकते हैं। कृपया YouTube चैनल को भी देखें।

YouTube Video