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 appjs/: archivos JavaScriptappid.js: el punto de entrada de la app
css/: ubicación de todos los archivos CSSappid.css
Al usar un empaquetador para compilar Typescript (o JavaScript), recomendamos la siguiente estructura:
appid/: raíz de la appjs/: archivos JavaScript compiladoscss/: salida CSS compiladasrc/: raíz de todos los archivos fuentecomponents/: ubicación de los componentes Vuecomposables/: ubicación de los composables de Vueservices/: ubicación de los archivos de servicios, como las abstracciones de APIstores/: ubicación de los stores de Piniaviews/: ubicación de las vistasmain.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 decallHTTPAPI().Los subcomponentes deben llevar prefijo. P. ej., al dividir un componente como
FileListEntryen componentes más pequeños llamadosFileListEntryName,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
SettingssinoSettingsViewoUserSettings, etc.
✅ Hacer |
❌ No hacer |
|---|---|
const fileId = 123
const obj = {
myProperty: false,
}
doSomething()
|
const file_id = 123
const obj = {
'my-property': false,
}
do_something()
|
✅ 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¶
✅ Hacer |
❌ No hacer |
|---|---|
const text = 'foo'
doSomething()
|
const text = 'foo';
doSomething();
|
const text = 'foo'
;(someProp as SomeType).handle()
|
Cadenas¶
✅ Hacer |
❌ No hacer |
|---|---|
const text = 'foo'
|
const text = "foo"
|
✅ Hacer |
❌ No hacer |
|---|---|
const text = `Hello ${username}!`
|
const text = 'Hello ' + username
|
Arrays¶
✅ Hacer |
❌ No hacer |
|---|---|
const arr = [
'first',
'second',
'third',
]
|
const arr = ['first', 'second', 'third']
|
✅ 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
functionse elevan (hoisting), así que pueden usarse incluso en otras funciones situadas antes de su definición. Además, usar la palabra clavefunctionhace 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 dethis.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.
✅ Hacer |
❌ No hacer |
|---|---|
doSomething(1, false)
|
doSomething (1, false)
|
✅ 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
}
|
✅ 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,
) {
// ...
}
|
✅ 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}`
})
|
✅ Hacer |
❌ No hacer |
|---|---|
myArray.map((item) => item.name)
|
myArray.map(item => item.name)
|
myArray.map((item, index) => getName(item, index))
|
✅ 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¶
✅ Hacer |
❌ No hacer |
|---|---|
const obj = {
noQuotesNeeded: true,
'quotes-needed': false,
}
|
const obj = {
'noQuotesNeeded': true,
'quotes-needed': false,
}
|
✅ Hacer |
❌ No hacer |
|---|---|
const name = 'jdoe'
// ...
const obj = {
name,
id: 123,
}
|
const name = 'jdoe'
// ...
const obj = {
name: name,
id: 123,
}
|
✅ Hacer |
❌ No hacer |
|---|---|
const obj = {
first: 1,
second: 'two',
}
|
const obj = { first: 1, second: 'two' }
|
✅ Hacer |
❌ No hacer |
|---|---|
const obj = { prop: true }
|
const obj = {prop: true}
|
✅ 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
✅ 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
✅ 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
|
✅ Hacer |
❌ No hacer |
|---|---|
if (something === 'something'
|| condition2
&& condition3
) {
// your code
}
|
if (something === 'something' || condition2 && condition3) {
// your code
}
|