טייפסקריפט 5.2 הגיעה פקודה חדשה בשם using שתפקידה לאפשר לנו לנקות אוטומטית משאבים, קצת כמו with של פייתון. המנגנון בנוי בשתי שכבות:
מקום אחד בקוד אחראי על יצירת המשאבים. אותו מקום שמקצה את המשאבים גם מגדיר את פונקציית הניקוי שלהם.
מקום אחר בקוד "מפעיל" את המקום שיוצר משאבים, מקבל משאב ובאופן אוטומטי טייפסקריפט מזהה מתי המשאב שיצרנו יוצא מ scope ומנקה אותו.
טכנית זה עובד עם Symbol מיוחד בשם [Symbol.dispose]
. הקוד שיוצר משאבים מחזיר אוביקט שאחד המפתחות בו הוא אותו Symbol.dispose, הערך של המפתח הזה הוא פונקציית הניקוי וכשצריך למחוק את המשאב טייפסקריפט יפעיל את הפונקציה.
דוגמה פשוטה לקוד שפותח קובץ נראית כך:
import { open } from "node:fs/promises";
const getFileHandle = async (path: string) => {
const fh = await open(path, "w");
return {
fh,
[Symbol.asyncDispose]: async () => {
await fh.close();
},
};
};
הפונקציה פותחת קובץ ומחזירה גם את הקובץ וגם את פונקציית הסגירה באוביקט אחד, כשפונקציית הסגירה נשמרת במפתח Symbol.dispose או אם היא אסינכרונית כמו בדוגמה זה יהיה Symbol.asyncDispose.
במקום אחר בקוד אנחנו יוצרים את המשאב עם המילה החדשה using:
{
await using file = await getFileHandle("thefile.txt");
const { fh } = file;
fh.write('hello world');
}
וכך ביציאה מהבלוק טייפסקריפט אוטומטי יודע להפעיל את פונקציית הניקוי. קוד הטייפסקריפט בדוגמה יהפוך לקוד ה JavaScript הבא:
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
if (value !== null && value !== void 0) {
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
var dispose, inner;
if (async) {
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
dispose = value[Symbol.asyncDispose];
}
if (dispose === void 0) {
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
dispose = value[Symbol.dispose];
if (async) inner = dispose;
}
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
env.stack.push({ value: value, dispose: dispose, async: async });
}
else if (async) {
env.stack.push({ async: true });
}
return value;
};
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
return function (env) {
function fail(e) {
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
env.hasError = true;
}
var r, s = 0;
function next() {
while (r = env.stack.pop()) {
try {
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
if (r.dispose) {
var result = r.dispose.call(r.value);
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
}
else s |= 1;
}
catch (e) {
fail(e);
}
}
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
if (env.hasError) throw env.error;
}
return next();
};
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
});
import { open } from "node:fs/promises";
const getFileHandle = async (path) => {
const fh = await open(path, "w");
return {
fh,
[Symbol.asyncDispose]: async () => {
await fh.close();
},
};
};
{
const env_1 = { stack: [], error: void 0, hasError: false };
try {
const file = __addDisposableResource(env_1, await getFileHandle("thefile.txt"), true);
const { fh } = file;
fh.write('hello world');
}
catch (e_1) {
env_1.error = e_1;
env_1.hasError = true;
}
finally {
const result_1 = __disposeResources(env_1);
if (result_1)
await result_1;
}
} // Automatically disposed!
ולמה אני מספר לכם את כל זה עכשיו? הסיבה פשוטה ובכותרת. הפיצ'ר הגיעה השבוע ל node.js ותוכלו להשתמש בו גם בלי טייפסקריפט עם node 24. אני חושב שיש פה מדיניות של node.js לתמוך בטייפסקריפט בלי לקמפל לטייפסקריפט - מדיניות שהתחילה עם מנגנון ה strip types שלהם וממשיכה עם טיפול בפקודות הטייפסקריפט שלא קשורות רק לטיפוסים. עכשיו נשאר לחכות ל enum.