javascript - ¿Es necesario importar las mejoras globales como un efecto secundario en el consumidor?
Tengo un paquete llamado core.error. En este paquete tengo dos archivos
global.d.ts
export {};
declare global {
export interface Error {
foo(): void;
}
}
index.ts
Error.prototype.foo = function (this: Error): void {
};
export const dooFoo = (err:Error) : void => {
err.foo();
}
Ahora tengo este paquete como dependencia en mi segundo proyecto. Parece que el código de aumento global solo se ejecuta cuando hago esto.
import "core.error"
const err = new Error();
err.foo();
Pero cuando intento importar solo la función dooFoo, obtendré un TypeError en tiempo de ejecución que dice "TypeError: err.foo no es una función".
import { dooFoo } from "core.error"
const err = new Error();
err.foo();
¿Es este un comportamiento esperado? Si es así, ¿necesito importar dos veces desde mi módulo así?
import "core.error"
import { dooFoo } from "core.error"
const err = new Error();
dooFoo(err.foo());
¿Existe alguna otra solución para cargar y ejecutar correctamente mi código de módulo que modifica los globals?
Actualizar
Lecturas realizadas antes de preguntar
- https://www.bennadel.com/blog/3290-using-module-augmentation-to-safely-inject-runtime-methods-using-typescript-and-node-js.htm
- https://www.typescriptlang.org/docs/handbook/declaration-merging.html
- https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Declaraciones/import#importar_un_módulo_solo_por_sus_efectos_secundarios
- ¿Cómo se añaden polyfills a Globals en TypeScript (módulos)?
Actualización 2
El archivo de declaración para index.ts se ve así
/// <reference types="node" />
export declare const dooFoo : (error: Error ) => void;
tsconfig.json se ve así
{
"compilerOptions": {
"module": "ES2022",
"target": "ES2022",
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"strict": true,
"outDir": "./build"
},
"include": ["source"],
"exclude": ["node_modules", "tests", "build"]
}
Respuesta #1
TypeScript eliminará imports que detecte (correcta o incorrectamente) como no utilizados en el módulo que los importa. Eso significa que cuando se compile a JavaScript, la declaración import estará completamente ausente.
En tu ejemplo importas dooFoo pero nunca lo usas. Y entonces TypeScript piensa que la importación es inútil y la elimina. Pero TypeScript no lleva un registro de si un módulo importado tiene efectos secundarios o no. Así que la importación eliminada conduce al error en tiempo de ejecución que estás viendo.
El enfoque actualmente recomendado es usar la opción del compilador --verbatimModuleSyntax tal como se lanzó con TypeScript 5.0 y se implementó en microsoft/TypeScript#52203. Esto esencialmente desactiva la elisión de importaciones (con la excepción de las importaciones que usan el modificador type, donde el desarrollador solicita explícitamente la elisión de importaciones). Antes de TypeScript 5.0 existían otras opciones del compilador que se podían usar para lograr este efecto, pero ya no son relevantes y han sido desaprobadas.
Si activas --verbatimModuleSyntax, entonces tu importación de dooFoo está presente en el JavaScript compilado y obtienes el comportamiento de efecto secundario deseado.
Enlace de Stackblitz al código
Si no puedes usar este enfoque, entonces tendrás que buscar una solución alternativa. Una forma es, como se menciona en la pregunta, escribir explícitamente import "core.error" para realizar una importación solo por sus efectos secundarios, lo cual TypeScript entiende como que tiene efectos secundarios.
Otra forma sería dejar la importación como está pero realmente usar dooFoo en tu código para que TypeScript no piense que puede omitir nada:
import { dooFoo } from "core.error"
dooFoo; // <-- mention it somewhere
const err = new Error();
err.foo();
Enlace de Stackblitz al código
Esta última forma es probablemente el enfoque alternativo más normal, ya que presumiblemente vas a usar realmente una función importada en algún lugar de tu código.
Pero recomendaría usar --verbatimModuleSyntax ya que hace que TypeScript se comporte de manera más consistente, y solo se obtiene elision cuando lo solicitas explícitamente con una declaración import type.
