Egy átfogó útmutató a TypeScript “modulrendszeréről” (példákkal)
On október 16, 2021 by adminModulszabvány
Az EcmaScript szabványosította, hogyan kell modulokat importálnunk a JavaScriptben, és ez a szabvány két kulcsszó, a import
és a export
körül forog. Mivel a modulrendszer a JavaScriptben egyre népszerűbb, minden évben újabb és újabb változtatások érkeznek ehhez a szabványhoz.
Ezzel a bemutatóval a TypeScript modulrendszerének szemantikáját fogjuk megvizsgálni (amely leginkább az EcmaScript szabványra hasonlít), és azt, hogyan működik a import
és a export
kulcsszó.
Nevezett export
A export
kulcsszó egy fájlban meghatározott értéket (változót) elérhetővé tesz más modulfájlokban történő importálásra. Amikor egy modult egy másik modulfájlba importálunk a import
kulcsszó használatával, az importáló fájl megadhatja azokat az értékeket, amelyekhez az importált modulból hozzá akar férni.
// program.ts
import { A } from 'path/to/values';console.log( A ); // "Apple"
A fenti példában a program.ts
fájlban a values.ts
értéket importáljuk, és a A
exportált tagot kivonjuk. Ez azt jelenti, hogy a values.ts
-nek valamilyen formában exportálnia kell a A
értéket. A A
exponálásának több módja is van.
// values.ts
var A = "Apple"; // can also use `let` or `const`
class B {}
function C(){}
enum D{}export { A, B, C, D };
Exportálhat bármilyen elérhető TypeScript értéket érvényes azonosítóval (névvel). Itt ebben a példában egy A
változót (lehet egy konstans is), egy B
osztályt és így tovább exportálunk. A export {}
szintaxis használatával annyi értéket exportálhat, amennyit csak akar, és bárki importálhatja őket a import { A, B }
szintaxis használatával. Nem kell az összes exportált értéket importálni.
Az értékek exportálásának van egy másik módja is, ahol deklarálták őket.
// values.ts
export const A = { name: "Apple" };
export class B {}
export function C(){}
export enum D{}
A fenti példában az összes értéket ott exportáltuk, ahol deklaráltuk őket. Az értéket sem kell inicializálni, ezt a fájlban később is megtehetjük. Ez egy sokkal szebb megoldás az előzőhöz képest.
Mindkét esetben az összes exportált tagunk elérhető ugyanabban a fájlban, ha szükségünk van rájuk. Az, hogy export
kulcsszó van egy értéken, nem változtatja meg annak viselkedését a modulfájlon belül.
Default Export
Eleddig olyan értékeket exportáltunk, amelyeket csak az export tagok nevének megadásával lehet importálni, mint például import { A } from 'path/to/values'
, ahol A
itt a values.ts
export tagja. Ezeket nevezzük nevesített exportnak.
Egyetlen értéket is exportálhatunk, amely név megadása nélkül importálható. Ezt az értéket a default
kulcsszóval kell azonosítani a export
kulcsszóval együtt.
// values.ts
var A = "Apple"; // can also use `let` or `const`
class A {}
function A(){}
enum A{}export default A;
Egyetlen értéket lehet alapértelmezett exportként exportálni, ezért nincs export default {...}
kifejezés, amivel dolgozhat. A név szerinti exportálással ellentétben egy értéket nem lehet alapértelmezettként exportálni egy változó (vagy konstans) deklarációval, kivéve a function
és class
deklarációt.
// values.ts
export default var A = "Apple"; // ❌ invalid syntax
export default enum D{} // ❌ illegal: not a function or class
export default class B {} // ✅ legal
export default function C(){} // ✅ legal
Viszont egy értéket közvetlenül exportálhatunk alapértelmezett exportként. Ez azt jelenti, hogy bármilyen JavaScript-kifejezést exportálhatunk alapértelmezett exportértékként.
// values.ts
export default "Hello"; // string
export default {}; // object
export default () => undefined; // function
export default function(){}; // function
export default class{}; // class
Mivel az alapértelmezett exportnak nincs exportnév, importáláskor bármilyen nevet megadhatunk neki. Ugyanabban a modulfájlban lehetnek megnevezett exportok és egy alapértelmezett export is. Hasonlóképpen importálhatjuk az alapértelmezett export és a megnevezett export értékeit egyetlen import
deklarációban, ahogy az alább látható.
import Apple, { B, C } from 'path/to/values';
A fenti példában a Apple
az a név, amellyel az alapértelmezett exportot a values.ts
-ből gyűjtöttük össze. A { B, C }
kifejezést vagy a Apple
nevet elhagyhatja a import
deklarációból, ha nem fogja használni.
💡 A megnevezett exportokhoz hasonlóan a
import
deklaráció használata esetén sem kötelező az alapértelmezett exportérték importálása. De aApple
-nek aimport
deklaráció aláírásában a megnevezett exportok előtt kell állnia.
A as default
szintaxist is használhatja a export {}
aláírásában, hogy egy értéket alapértelmezettként exportáljon más megnevezett exportokkal együtt. Ezt aliasingnek hívják, és a következő szakaszban magyarázzuk el.
// values.ts
var A = "Apple"; // can also use `let` or `const`
class B {}
function C(){}
enum D{}export { A as default, B, C, D };
💡 Az ok, amiért nem használom és nem ajánlom a
default
exportot, a fejlesztői tapasztalatokkal függ össze. Mint tanultuk, aimport
deklarációban bármilyen nevet megadhatunk egy modul alapértelmezett export tagjának. Ha ezt az értéket több fájlba importáljuk, akkor tetszőleges névvel hivatkozhatunk rá, és az, hogy ugyanazon modul ugyanazon exporttagjának különböző nevei vannak, rossz DX.
Import And Export Alias
Az alapértelmezett export esetében a import
deklarációban tetszőleges névvel hivatkozhatunk az értékre. Ez azonban nem így van a nevesített exportok esetében. A as
kulcsszóval azonban tetszőleges névvel hivatkozhat a megnevezett exportértékre.
// program.ts
import A, { B as Ball } from 'path/to/values';console.log( B ); // ❌ Error: Cannot find name 'B'.
console.log( Ball ); // ✅ legal
A fenti példában a B
értéket importáljuk a values.ts
-ből, de azt átneveztük Ball
-re. Ezért a program.ts
fájlban a B
helyett a Apple
-et használnánk. Amikor egy exporttagot aliasol, az eredeti név már nem létezik a globális hatókörben, ezért a B
már nem fog létezni a program.ts
fájlban.
💡 Az alapértelmezett exporttagot nem aliasolhatja, mivel már bármilyen tetszőleges nevet megadhat, de a
import { default as defaultValue, }
-t használhatja.
Az exportálás során az as
kulcsszó használatával alias értéket is megadhatunk.
// values.ts
var A = "Apple"; // can also use `let` or `const`
class B {}
function C(){}
enum D{}export { A as default, B, C as Cat, D };
A fenti példában a A
változót alapértelmezett exportként, a C
függvényt pedig Cat
ként exportáltuk. Ezért bárkinek, aki ezeket az értékeket a values.ts
fájlból importálja, a A
értékhez az alapértelmezett import szintaxist, a C
értékhez pedig a szintaxist kell használnia.
Minden megnevezett export importálása
Egy fájlból az összes megnevezett exportot a * as
szintaxis használatával importálhatja.
// program.ts
import Apple, * as values from 'path/to/values';console.log( values.B );
console.log( values.C );
console.log( values.D );
Itt az összes névre szóló export a values.ts
-ből a values
alá lesz mentve, amely egy objektum lesz, ahol a keys
az exportált tagok neve, a values
pedig az exportált tagok exportált értékei lesznek.
Újraexportálás
Egy importált értéket a szokásos módon lehet újraexportálni. Ha valamit importálsz, akkor van egy hivatkozásod az importált értékre, és ugyanezt az értéket használhatod a export
szintaxisban. Ezzel nincs semmi baj.
// lib.ts
import A, { B, C as Cat, D } from 'path/to/values';export { D as default, A, B, Cat as C, D };
A fenti példában a values.ts
-ből importáltál néhány értéket a lib.ts
-en belül, és tetszés szerint exportáltál. Ez nagyszerű, ha ezeket az importált értékeket a lib.ts
-ben szeretné használni, valamint néhányat exportálni, ha egy másik fájlnak szüksége van rájuk, amikor a lib.ts
-ből importál.
Megvan azonban a export ... from
utasítás is, amellyel exportálhatunk értékeket egy fájlból anélkül, hogy először importálnunk kellene őket. Ez akkor nagyszerű, ha egyetlen belépési pontot szeretnénk tartani a modulunk összes importált értékéhez.
// lib.ts// re-exports
export { P as Paper, Q } from 'path/to/values-1';
export { N, O as Orange } from 'path/to/other/values-2';// default export
export default "hello";class B {}
function C(){}// named exports
export { B, C as Cat };
A fenti példában a values-1.ts
és a values-2.ts
exportált tagjainak egy részét exportáljuk a lib.ts
fájlból (kivéve az alapértelmezett exportot). A export ... from
szintaxisban is használhatunk aliasinget.
A re-export szintaxis nem érinti az aktuális fájlt, ezért a fenti példában látható módon a fájlban a szokásos exportjaink is lehetnek. A export
kulcsszóval ellentétben a export ... from
szintaxis nem enged hozzáférést az újraexportált tagokhoz. Ezért nem fog tudni hozzáférni a P
vagy Orange
tagokhoz a lib.ts
fájlban.
// lib.ts
export { P as Paper, Q } from 'path/to/values-1';
export { N, O as Orange } from 'path/to/other/values-2';console.log( P ); // ❌ Error: Cannot find name 'P'.
console.log( Orange ); // ❌ Error: Cannot find name 'Orange'.
A export * from
szintaxissal exportálhatja az összes megnevezett exportált tagot egy fájlból, mivel a export * as ... from
szintaxis segítségével átnevezheti őket exportálás közben.
// lib.ts
export * from 'path/to/values-1';
export * as values2 from 'path/to/other/values-2';
A fenti példából kiindulva, ha a values-1.ts
exportálja a P
és Q
neveket, akkor bárki importálhatja a P
és Q
neveket a lib.ts
címről. Azonban a values-2.ts
összes nevesített exportja a lib.ts
nevesített values2
exportjaként kerül újraexportálásra, ezért ezeket a következő szintaxissal kell importálni.
// program.ts
import { P, Q, values2 } from 'path/to/lib';
Az alapértelmezett exportot a export ... from
szintaxissal normál módon nem lehet elérni. De exportálhatja az alapértelmezett exportot a default
kulcsszó használatával.
// lib.ts
export { default } from 'path/to/values-1';
export { default as Apple } from 'path/to/values-2';
A fenti példában a lib.ts
alapértelmezett export értéke a values-2.ts
alapértelmezett export értéke. Az values-2
alapértelmezett export értéke a lib.ts
-ből a Apple
nevű exportként lesz exportálva.
Import mellékhatásért
El tudod képzelni, hogy egy fájlt úgy importálsz, hogy nem adod meg az exportált tagokat?
import 'path/to/action';
A fenti példában a action.ts
importáljuk, de megadtunk minden olyan tagot, amelyet a action.ts
exportál. Ez egy érvényes szintaxis, de nagyon eltérő eredményt kapunk vele.
Az import
kulcsszóval történő importáláskor először végrehajtódik, majd az összes exportált tag importálhatóvá válik. Ezért az alábbi példában egy PI
importáló fájl a 3.14
-t kapná floating-point
számként.
export const PI = parseFloat( "3.14" ); // 3.14
Az ugyanezt a logikát követve, ha egy fájl importálásával akarsz egy mellékhatást létrehozni, akkor azt teljesen megteheted. Így például, ha van egy fájl, amely inicializál néhány globális változót, akkor azt importálhatjuk anélkül, hogy megadnánk a fájl exportált tagjait (az is lehet, hogy eleve nem exportál semmit).
A TypeScript fordító képes létrehozni egy JavaScript bundle fájlt (.js
) két vagy több .ts
fájl összefordításával. Ez általában úgy történik, hogy megad egy belépő TypeScript fájlt, majd végigmegy annak függőségi fáján.
A függőségi fa úgy épül fel, hogy megnézzük a belépő fájl összes import
deklarációját (függőségét), és követjük ezeknek az importált fájloknak a függőségeit. Ha be akarunk vonni egy olyan fájlt, amelynek kódja futásidőben mellékhatásokat okozhat, akkor azt a fájlt az exportált tagok megadása nélkül kell importálni a import
szintaxisban. Ez megtehető a belépési fájlban vagy bármely olyan fájlon belül, amely a függőségi fán belül van.
Vélemény, hozzászólás?