JavaScript tipizzato senza utilizzare TypeScript

Vediamo come utilizzare JsDoc per avere una tipizzazione in stile TypeScript anche su JavaScript

Utilizzare TypeScript in un progetto ci porta grandi vantaggi, specialmente se stiamo lavorando su qualcosa di particolarmente complesso. Grazie alla tipizzazione di TypeScript possiamo infatti scrivere codice di qualità superiore, questo perché possiamo accorgerci prontamente di eventuali errori ed avere un miglior supporto da parte dell'editor.

Esistono però alcuni casi in cui non abbiamo la possibilità di utilizzare per TypeScript oppure semplicemente stiamo lavorando a un progetto molto semplice e non vogliamo andare a complicarlo eccessivamente ma vogliamo comunque avere un miglior supporto per i tipi. In casi come questi possiamo utilizzare JsDoc.

JsDoc è linguaggio di markup che viene utilizzato nei commenti per annotare il codice all'interno dei file JavaScript. I file vengono successivamente analizzati con dei particolari strumenti per estrapolarne tutte le informazioni e generare così la relativa documentazione. Quasi tutti gli editor di testo moderni effettuano questo processo in background e quindi riescono a mostrarci le informazioni estrapolate in tempo reale.

# Abilitare la tipizzazione in VsCode

Visual Studio Code ha un ottimo supporto per TypeScript e JsDoc e questo ci permette di scrivere codice JavaScript tipizzato senza utilizzare TypeScript. Se vogliamo quindi tipizzare il nostro codice JavaScript dobbiamo come prima cosa abilitare l'intellisense di typescript all'interno dei file js, per fare questa operazione dobbiamo attivare l'impostazione javascript.implicitProjectConfig.checkJs: true oppure possiamo aggiungere un commento // @ts-nocheck in cima al file interessato.

Ovviamente non possiamo utilizzare tutte le funzionalità che abbiamo normalmente a disposizione in TypeScript, ma sono comunque abbastanza da migliorare notevolmente la qualità del nostro codice.

# TypeScript vs JsDoc

Di seguito troviamo alcuni esempi delle funzionalità più utili di JsDoc e il relativo confronto con la versione TypeScript:

# Assegnare un tipo

I tipi nativi disponibili sono: number, string, undefined, Array, Object.

// TypeScript
const name: string = "Giovanni"

// JsDoc
/**
 * @type {string} name Il mio nome
 */
const name = "Giovanni"

Per gli array possiamo inoltre utilizzare: any[], string[], number[], Object[].

# Unioni e intersezioni

// TypeScript
const day: string | number = 7

// JsDoc
/**
 * @type {number | string} Giorno del mese
 */
const day = 7 // o "7"

// TypeScript
const car: { brand: string; fuel: number } = {
  brand: "Ferrari",
  fuel: 7.5
}

// JsDoc
/**
 * @type {{brand: string}, {fuel: number}}
 */
const car = {
  brand: "Ferrari",
  fuel: 7.5
}

# Interfacce e tipi personalizzati

In JsDoc non esiste una defizione dedicata per le interfacce, possiamo utilizzare un oggetto personalizzato per ovviare a questa mancanza.

// TypeScript
interface Vehicle {
  type: string
  color: string
}

// JsDoc
/**
 * Rappresenta un veicolo con tipo e colore
 * @typedef {Object<string, any>} Vehicle
 * @property {string} type Il tipo di veicolo
 * @property {string} color Il colore del veicolo
 */
/**
 * @type {Vehicle} vehicle
 */
const vehicle = {
  type: "card",
  color: "red"
}

# Metodi e funzioni

In JsDoc abbiamo a disposizione @function e @method per indicare funzioni e metodi, ma la maggior parte delle volte ci basterà specificare il tipo Function, i relativi parametri e tipo di ritorno

// TypeScript
function sum(a: number, b: number): number {
  return a + b
}

// JsDoc
/**
 * Somma due numeri e restituisce il risultato
 * @param {number} a - Primo numero
 * @param {number} b - Secondo numero
 * @returns {number} La somma di a e b
 */
function sum(a, b) {
  return a + b
}

# Tipi generici

Utilizzando @template possiamo definire tipi generici

// TypeScript
function identity<T>(arg: T): T {
  return arg
}

// JsDoc
/**
 * @template T
 * @param {T} arg - Parametro di tipo generico che verrà usato nel valore di ritorno
 * @return {T}
 */
function identity(arg) {
  return arg
}

# Casting dei tipi

Occasionalmente potremmo avere la necessità di forzare il tipo di un dato, per fare questa operazione ci basterà anteporre un @type

// TypeScript
let a: string = "1"
;(a as number) = 1

// JsDoc
/**
 * @type {string} a
 */
let a = "1"

/** @type {number} */ a = 1

# Importare i tipi

Se vogliamo importare il tipo da un altro file possiamo utizzare la funzione Import

// TypeScript
import { Vehicle } from "./types"

const vehicle: Vehicle = {
  type: "card",
  color: "red"
}

// JsDoc
/**
 * @typedef {import('./types').Vehicle} Vehicle
 */

/**
 * @type {Vehicle} vehicle
 */
const vehicle = {
  type: "card",
  color: "red"
}

# L'estensione Document This

Nel marketplace di Visual Studio Code troviamo un estensione molto utile che riesce a generare i commenti JsDoc partendo da una porzione di codice JavaScript, quindi banalmente ci basterà selezionare la parte di codice interessata e premere Ctrl+Alt+D.