קובץ Type Definition בטייפסקריפט

הסבר על הדרך של טייפסקריפט לתת לנו מידע על הטייפים של מודולים חיצוניים.
לוגו טייפסקריפט

המאמר הזה הוא מאמר הסיום בסדרת המדריכים על טייפסקריפט. במאמר הקודם דיברנו על מודולים עם טייפסקריפט.

קבצי Type definitions שהסיומת שלהם היא d.ts עוזרים לנו כאשר אנו צורכים קבצי ג׳אווהסקריפט לתוך פרויקט הטייפסקריפט שלנו.

מתי זה קורה? בדרך כלל זה קורה כאשר אנו עושים import לחבילת npm או קובץ ג׳אווהסקריפט. גם אם המודול נכתב בטייפסקריפט, התוצאה המקומפלת היא בג׳אווהסקריפט. ואז אני בבעיה כי אני צריך את הטייפים של החבילה/מודול ואין לי – כי בניגוד לקוד טייפסקריפט שאני כותב והוא חלק מהפרויקט שלי – פה מדובר בפרויקט חיצוני שלא עובר קימפול או טרינספול כמו הקוד שלי.

קבצי Type definitions מסייעים לי בזה. אני אסביר ואדגים עם lodash (פרויקט עזר הכולל מתודות סיוע רבות לג׳אווהסקריפט) ופרויקט ריאקטי שיצרתי עם Vite.

נכון לזמן כתיבת שורות אלו, lodash הוא פרויקט ג׳אווהסקריפטי לגמרי וכאשר אני עושה לו npm install, אני משתמש וצורך את הפורמט הג׳אווהסקריפטי שלו. קל לראות את זה – אם אני אעשה npm install lodash אז אני יכול להכנס לתיקית node_modules ולראות מה יש ב-index שלו. יש require ל lodash.js ובקובץ שם יש קוד ב… ג׳אווהסקריפט. זה מה שאני צורך גם אם אני משתמש בטייפסקריפט:

קוד ג׳אווהסקריפט בלודאש - הקוד עצמו לא משנה. מה שחשוב הוא שרואים שזה ג׳אווהסקריפט נייטיב.
קוד ג׳אווהסקריפט בלודאש

למה זה חשוב לי? ראשית, אם אני אנסה לכתוב קוד כזה:

import _ from 'lodash';

// Next, define an array of numbers:
const numbers = [1, 2, 3, 4, 5];

// Use the `_.map()` function to double the values in the array, and save the result in a new array:
const doubled = _.map(numbers, (num:number) => num * 2);

// The `doubled` array should now contain the values [2, 4, 6, 8, 10].

console.log(doubled.split(' '))

export {};

אני אקבל שגיאה מיידית ב-Visual Studio Code:

Could not find a declaration file for module 'lodash'. '/Users/barzik/local/demos/demo-vite-project/node_modules/lodash/lodash.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/lodash` if it exists or add a new declaration (.d.ts) file containing `declare module 'lodash';`ts(7016)
שגיאת טייפסקריפט שנובעת משימוש בלודאש ישירות ללא טייפים

מה זה אומר? זה אומר בעצם שטייפסקריפט לא יודעת לאכול את קוד ג׳אווהסקריפט שבעצם אנו מנסים לצרוך ללא טייפים. אין את חתימת הפונקציות וטייפסקריפט לא יודע למשל מה הפונקציה map של לודאש מחזירה ולא תוכל לוודא שאין בעיות בזמן ריצה.

אפשר להורות לטייפסקריפט להתעלם מהבעיה הזו באמצעות שימוש ב tsconfiig ותחת compilerOptions לשנות את strict ל-false:

{
  "compilerOptions": {
    "target": "ESNext",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "lib": ["ESNext", "DOM"],
    "moduleResolution": "Node",
    "strict": false,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "esModuleInterop": true,
    "noEmit": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}

זה בעצם גורם לטייפסקריפט לקבל קוד של ג׳אווהסקריפט גם אם הוא לא בא עם טייפים. מה הבעיה עם ה… פתרון הזה? שאז בעצם אין שום בדיקה של טייפים. פונקצית map מחזירה מערך. אם אני אנסה להשתמש בפונקצית split שאינה קיימת במערך, אני אקבל שגיאה בזמן ריצה! הקוד שלעיל, לא יציג שום שגיאה ב-Visual Studio Code או בטרנספיילר של טייפסקריפט, אבל כשאני אטען את הדף, אני אקבל שגיאה.

Uncaught TypeError: doubled.split is not a function
    at demo.ts:11:21

וזה בדיוק, אבל בדיוק מה שטייפסקריפט אמורה למנוע!

אז מה הפתרון? הפתרון הוא לוודא שיש לנו את הטייפים. איך מוודאים? מתקינים את הטייפים ידנית! איך? Visual Studio code כבר נותן אינדיקציה. עם:

npm i --save-dev @types/lodash

ואז קורים שני דברים, אין לנו שגיאה ליד ה-lodash, כי יש טייפים. ויש לנו כבר שגיאה ליד השימוש השגוי ב-split. כי כבר טייפסקריפט יודעת ש-map מחזיר מערך שלא יכול לעבוד עם סקריפט.

שגיאת טייפסקריפט המציגה הערה על split

וזה מה שבעצם אנחנו צריכים.

להכנס פנימה לנושא ה Type definitions

אבל מה קורה מאחורי הקלעים? מאחורי הקלעים כשאנו מתקינים את @types/lodash אנו מתקינים Type definition. אותו d.ts מסתורי שכל מה שיש בו, אבל ליטרלי כל מה שיש פה, זה טייפים. ואם תכנסו אל התיקיה של @types של הפרויקט תוכלו לראות בדיוק שמדובר בטייפים. משהו שאתם מכירים מטייפסקריפט ויש שם את ההגדרה של כל הפונקציות והמשתנים החשופים שיש ללודאש.

זה נעשה כי הסקריפט של לודאש הוא בג׳אווהסקריפט ואין שם טייפים אז אנחנו צריכים להביא אותם. בדרך כלל אם אנו משתמשים בבילד מסודר כמו create react app, או next.js , או vite או כל דבר אחר – הטייפים יכנסו לשם באופן אוטומטי כשאנו מתקינים את הטייפים.

בחלק מהספריות יש כבר Type definition במודול עצמו. זה קורה כי:

  1. האנשים מאחורי הספריה/מודול הבינו שהם צריכים לייצר קובץ Type definition כי משתמשים בטייפסקריפט ולא לסמוך על מודול חיצוני שמישהו אחר יספק והם אלו שידאגו שאיזה d.ts יהיה בספריה.
  2. הספריה/מודול שאנו צורכים מפותח בטייפסקריפט ובתהליך הטרינספול לטייפסקריפט אנו יכולים להוסיף ב tsconfig בקשה לייצר Type Definitions.

על מנת להמחיש את זה, ניצור ספריה פשוטה בטייפסקריפט. הנה משהו באמת… אה.. מטופש:

// index.ts
export function sum(a: number, b: number): number {
    return a + b;
  }

עם tsc –init אני אצור קובץ קונפיגורציה של טייפסקריפט ואייצא את הספריה שלי לתיקית dist. אם אני אפעיל את tsc, אז יווצר לי קובץ ג׳אווהסקריפט אחרי הקימפול (או יותר נכון טרנסיפלציה) :

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.sum = void 0;
// index.ts
function sum(a, b) {
    return a + b;
}
exports.sum = sum;

אם אני אייצא את המודול הזה ל-npm. מי שיצרוך אותו בטייפסקריפט לא ידע למשל לעולם שפונקצית sum מקבלת מספרים בלבד ומחזירה מספרים. כי הקוד שנצרך הוא ג׳אווהסקריפטי. א-ב-ל – אם אני אוסיף ל-tsconfig את הביטוי:

"declaration": true,   

ואריץ שוב את tsc, יווצר לי קובץ index.d.ts שיכיל את ההגדרה של הטייפים.

export declare function sum(a: number, b: number): number;

ה-index.d.ts של המודול המטופש שלי חי לו בשלום לצד הקובץ הג׳אווהסקריפטי. אבל מי שיצרוך את המודול שלי, יוכל לצרוך אותו ולקבל את הטייפים. איך אני אומר למי שצורך את הטייפים האלו איפה ה-d.ts שלי נמצא? באמצעות ה-package.json:

  "types": "index.d.ts"

סיכום

בגדול, רוב הפעמים, במיוחד כצרכן קצה, ה-Type Definition יהיו שקופים לנו. אנו צורכים את המודולים שלנו שברובם מכילים את הקבצים האלו או לחלופין, אם הם לא (כמו במקרה של LoDash), מישהו כבר הכין את הטייפים האלו ואנחנו יכולים לצרוך אותם בנחת מספק טייפים מרכזי כמו DefinitelyTyped וראינו דוגמה של זה. אם אתם מנסים לצרוך מודול חיצוני שאין בו Type definitions, נקבל שגיאה שאין טייפים ונצטרך לטפל בה. בפוסט הזה הסברתי למה וגם איך לדאוג לקובץ כזה אם אתם מייצרים ספריה משלכם.

סיכום הסדרה

המאמר הזה מסכם את סדרת הטייפסקריפט באתר. בסדרה הסברנו מה זה טייפסקריפט ובאמת המשכנו לאורך כל הפיצ׳רים החשובים בטייפסקריפט עד עכשיו. יש כמובן תמיד מה להעמיק ומה ללמוד עוד אבל מעבר על כל מדריכי הסדרה יספקו אפשרות לכל מתכנת ג׳אווהסקריפט להבין איך לכתוב בטייפסקריפט מייד.

פוסטים נוספים שכדאי לקרוא

רספברי פיי

מה זה AIoT? ואיך אפשר להתחיל?

פוסט עם המון קישורים, מידע, סרטונים ופרק בפודקאסט שיפתח לכם שער לעולם ה-AIoT המרתק.

ספריות ומודולים

מציאת PII באמצעות למידת מכונה

כך תגנו על משתמשים שלכם שמעלים מידע אישי רגיש כמו תעודות זהות באמצעות שירות אמאזוני.

גלילה לראש העמוד