JavaScript y Typescript

Resumen

Esta página recoge las reglas y los consejos para escribir JavaScript y Typescript: el uso de Vue y Typescript, la configuración compartida de ESLint, la estructura de archivos de una app y el estilo de código, con ejemplos de qué hacer y qué no. Está dirigida a quienes desarrollan apps.

Fuente: documentación de Nextcloud, «developer_manual/getting_started/coding_standards/javascript», versión 3ad9158, licencia CC BY 3.0; traducida y adaptada para APS Conecta Gestión.

Reglas y consejos generales

  • APS Conecta Gestión usa Vue.js para su interfaz; para una interfaz de usuario coherente, recomendamos que las apps también usen Vue con los componentes proporcionados. Aun así, también se pueden usar JavaScript y HTML puros.

  • Recomendamos usar Typescript por su comprobación de tipos y sus funciones mejoradas de análisis estático del código.

  • No crear variables globales; en su lugar, si hace falta, usar objetos de espacio de nombres globales como OCA.YourApp.…

  • Usar el modo estricto de JavaScript (es así automáticamente al usar módulos de JavaScript)

Configuración de ESLint

Hay una configuración compartida de eslint que se puede usar para formatear automáticamente el código JavaScript y Typescript de las apps de APS Conecta Gestión. Consta de dos partes: un paquete de configuración que contiene las preferencias de formato y un plugin para detectar las API obsoletas y eliminadas en el código. Consultar sus README para obtener instrucciones.

Estructura del sistema de archivos

Para JavaScript puro recomendamos la siguiente estructura:

  • appid/: raíz de la app

    • js/: archivos JavaScript

      • appid.js: el punto de entrada de la app

    • css/: ubicación de todos los archivos CSS

      • appid.css

Al usar un empaquetador para compilar Typescript (o JavaScript), recomendamos la siguiente estructura:

  • appid/: raíz de la app

    • js/: archivos JavaScript compilados

    • css/: salida CSS compilada

    • src/: raíz de todos los archivos fuente

      • components/: ubicación de los componentes Vue

      • composables/: ubicación de los composables de Vue

      • services/: ubicación de los archivos de servicios, como las abstracciones de API

      • stores/: ubicación de los stores de Pinia

      • views/: ubicación de las vistas

      • main.ts: punto de entrada principal de la app

Nombres de archivo

No tenemos reglas estrictas para los nombres de archivo: sirve tanto kebab case como camel case.

Aun así, recomendamos encarecidamente que las apps Vue sigan las recomendaciones de Vue y usen como nombre de archivo el mismo nombre del componente. P. ej., si el componente se llama AppRoot, el archivo debe llamarse AppRoot.vue.

Estilo de código

General

Nombres y mayúsculas
  • Usar camelCase para

    • funciones

    • métodos

    • propiedades

    • variables

  • Usar PascalCase para

    • clases

    • enums

    • tipos

    • interfaces

    • componentes Vue

  • Por legibilidad, poner en mayúscula solo la primera letra de las abreviaturas, como callHttpApi() en lugar de callHTTPAPI().

  • Los subcomponentes deben llevar prefijo. P. ej., al dividir un componente como FileListEntry en componentes más pequeños llamados FileListEntryName, FileListEntryIcon …

  • Los componentes no deben tener nombres de una sola palabra, ya que podrían entrar en conflicto con etiquetas HTML nativas actuales o futuras, que siempre son de una sola palabra. P. ej., si se tiene una vista de ajustes, no llamarla Settings sino SettingsView o UserSettings, etc.

Usar camelCase para funciones, métodos, propiedades y variables

✅ Hacer

❌ No hacer

const fileId = 123
const obj = {
    myProperty: false,
}
doSomething()
const file_id = 123
const obj = {
    'my-property': false,
}
do_something()
Usar PascalCase para clases, interfaces, tipos y componentes Vue

✅ Hacer

❌ No hacer

class MyClass { /* ... */ }
interface IRequest { /* ... */ }
type Arguments = string[]
class myClass { /* ... */ }
interface I_request { /* ... */ }
type arguments = string[]
Sangría
  • Usar tabulaciones en lugar de espacios para sangrar; el ancho de la tabulación es de 4 espacios.

    • Se pueden alinear, p. ej., los comentarios con espacios si hace falta.

Punto y coma
Evitar el punto y coma donde no sea necesario.

✅ Hacer

❌ No hacer

const text = 'foo'
doSomething()
const text = 'foo';
doSomething();
const text = 'foo'
;(someProp as SomeType).handle()

Cadenas

Usar comillas simples.

✅ Hacer

❌ No hacer

const text = 'foo'
const text = "foo"
Preferir las plantillas literales por legibilidad.

✅ Hacer

❌ No hacer

const text = `Hello ${username}!`
const text = 'Hello ' + username

Arrays

Evitar varias propiedades en la misma línea

✅ Hacer

❌ No hacer

const arr = [
    'first',
    'second',
    'third',
]
const arr = ['first', 'second', 'third']
Usar comas finales; esto reduce el diff al añadir propiedades nuevas.

✅ Hacer

❌ No hacer

const arr = [
    'first',
    'second',
    'third',
]
const arr = [
    'first',
    'second',
    'third'
]
const arr = [
    'first',
    'second',
+   'third',
]
const arr = [
    'first',
-   'second'
+   'second',
+   'third'
]

Funciones

  • Sin espacios entre el nombre de la función y los parámetros.

  • Llaves en la misma línea que la definición.

  • Usar saltos de línea coherentes en los parámetros (o todos en una línea, o un parámetro por línea).

  • Para las funciones de nivel superior, preferir las funciones normales a las funciones flecha. En Javascript, las funciones definidas con la palabra clave function se elevan (hoisting), así que pueden usarse incluso en otras funciones situadas antes de su definición. Además, usar la palabra clave function hace la definición más explícita, lo que favorece la legibilidad. Para los callbacks, las funciones flecha anónimas suelen ser más adecuadas, ya que no crean su propio enlace de this.

  • Usar siempre paréntesis en las funciones flecha. Esto ayuda a la legibilidad y evita problemas si se añaden parámetros.

  • Al usar valores de retorno implícitos en funciones flecha con un cuerpo de varias líneas, rodear el cuerpo con paréntesis.

Sin espacio entre el nombre de la función y los parámetros

✅ Hacer

❌ No hacer

doSomething(1, false)
doSomething (1, false)
Llaves en la misma línea que la definición.

✅ Hacer

❌ No hacer

function foo(name: string): boolean {
    // do something
}
function foo(name: string): boolean
{
    // do something
}
function bar(
    firstName: string,
    lastName: string,
): boolean {
    // do something
}
function bar(
    firstName: string,
    lastName: string,
): boolean
{
    // do something
}
const arrow = (name: string) => {
    // do something
}
const arrow = (name: string) =>
{
    // do something
}
Usar saltos de línea coherentes en los parámetros de las funciones

✅ Hacer

❌ No hacer

function doSomething(num: number, enable: boolean) {
    // ...
}
function doSomething(num: number,
    enable: boolean) {
    // ...
}
function doSomething(
    num: number,
    enable: boolean,
) {
    // ...
}
function doSomething(
    num: number, enable: boolean,
) {
    // ...
}
Preferir funciones normales en el nivel superior.

✅ Hacer

❌ No hacer

export function doSomething(num: number, enable: boolean) {
    // ...
}
export const doSomething = (num: number, enable: boolean) => {
    // ...
}
someArray.map((item) => item.name)
// or
someArray.map((item) => {
    return item.name
})
// while this is valid and work
someArray.map(function (item) {
    return item.name
})
// there is a caveat with accessing "this"
someArray.map(function (item) {
    // "this" is not the previous context
    // but the context of the callback function.
    // Thus this.category will be undefined.
    return `${this.category}: ${item.name}`
})
Usar siempre paréntesis en los parámetros de las funciones flecha.

✅ Hacer

❌ No hacer

myArray.map((item) => item.name)
myArray.map(item => item.name)
myArray.map((item, index) => getName(item, index))
Usar paréntesis en el cuerpo de varias líneas de las funciones flecha.

✅ Hacer

❌ No hacer

myArray.map((item) => (
    item.value
        ? 'yes'
        : 'no'
))
myArray.map((item) => item.value
    ? 'yes'
    : 'no'
)
myArray.map((item) => ({
    prop: item.value,
    other: true,
}))

Objetos

Poner comillas en las propiedades solo cuando haga falta.

✅ Hacer

❌ No hacer

const obj = {
    noQuotesNeeded: true,
    'quotes-needed': false,
}
const obj = {
    'noQuotesNeeded': true,
    'quotes-needed': false,
}
Preferir las propiedades abreviadas

✅ Hacer

❌ No hacer

const name = 'jdoe'
// ...
const obj = {
    name,
    id: 123,
}
const name = 'jdoe'
// ...
const obj = {
    name: name,
    id: 123,
}
Evitar varias propiedades en la misma línea

✅ Hacer

❌ No hacer

const obj = {
    first: 1,
    second: 'two',
}
const obj = { first: 1, second: 'two' }
Añadir espacios alrededor del contenido cuando haga falta

✅ Hacer

❌ No hacer

const obj = { prop: true }
const obj = {prop: true}
Usar comas finales; esto reduce el diff al añadir propiedades nuevas.

✅ Hacer

❌ No hacer

const obj = {
    first: 1,
    second: 2,
}
const obj = {
    first: 1,
    second: 2
}
const obj = {
    first: 1,
    second: 2,
+   third: 3,
}
const obj = {
    first: 1,
-   second: 2
+   second: 2,
+   third: 3
}

Operadores

  • Usar siempre === y !== en lugar de == y !=

  • Preferir las comparaciones explícitas

Este es el motivo:

'' == '0'           // false
0 == ''             // true
0 == '0'            // true

false == 'false'    // false
false == '0'        // true

false == undefined  // false
false == null       // false
null == undefined   // true

' \t\r\n ' == 0     // true
Usar comparaciones explícitas

✅ Hacer

❌ No hacer

if (array.length > 0) { /* ... */ }
if (array.length) { /* ... */ }
if (array) { /* this is always true! */ }

Estructuras de control

  • Usar siempre llaves, también en los if de una sola línea

  • Dividir los if largos en varias líneas

  • Usar siempre break en las sentencias switch y prevenir con advertencias un bloque default si no se debe acceder a él

Usar siempre llaves.

✅ Hacer

❌ No hacer

if (myVar === 'hi') {
    doSomething()
}
if (array.length > 0) doSomething()
for (let i = 0; i < 4; i++) {
    // your code
}
for (let i = 0; i < 4; i++)
    // your code
Dividir las condiciones largas en varias líneas.

✅ Hacer

❌ No hacer

if (something === 'something'
    || condition2
    && condition3
) {
    // your code
}
if (something === 'something' || condition2 && condition3) {
    // your code
}