일.구문 포맷
TypeScript 는 ES Module 규범과 호환성이 있으며, 파일이 모듈입니다
간단히 말해, 파일 중에 합법적인 import 또는 export 문이 포함되어 있으면, 모듈로 취급됩니다 (모듈 스코프를 가짐).否则글로벌 스코프 하에서 실행됩니다. 예를 들어:
let x = 1
function f() { }
// 会被编译成
var x = 1;
function f() { }
// 而
let x = 1
export function f() { }
// 会被编译成(以 AMD 形式为例)
define(["require", "exports"], function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var x = 1;
function f() { }
exports.f = f;
});
모든 선언은 import/export 가능하며, 인터페이스, 타입 별칭 등을 포함합니다:
export interface StringValidator {
isAcceptable(s: string): boolean;
}
export type PhoneNumber = string;
特殊的、순수한 선언 파일 (예를 들어 d.ts) 는 실제적인 의미가 있는 코드를 생성하지 않지만, 그래도 모듈 (스코프) 격리를 가집니다:
// 上例会被编译成
define(["require", "exports"], function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
});
이것도 d.ts 분류 의 근거의 1 つ입니다
P.S. import/export 具体구문은 ES Module 참조. 여기서는 전개하지 않습니다
CommonJS 모듈 서포트
[CommonJS 와 AMD 모듈](/articles/module-es6 笔记 13/#articleHeader2) 을 서포트하기 위해, TypeScript 는 특수한 구문을 제공합니다:
export = something;
모듈의 익스포트 오브젝트를 정의하는 데 사용되며, NodeJS 내의 다음과 유사:
// NodeJS 모듈 (CommonJS)
let x = {a: 1};
exports.x = x;
module.exports = x;
TypeScript 로 다시 쓰면 이렇습니다:
let x = {a: 1};
export = x;
// 会被编译成
define(["require", "exports"], function (require, exports) {
"use strict";
var x = { a: 1 };
return x;
});
대응하는 인포트 구문도 NodeJS(require('./myModule.js')) 와는 다릅니다:
import module = require("myModule")
이.모듈 코드 생성
컴파일 옵션 (--module 또는 -m) 을 통해 생성 코드의 모듈 포맷을 지정할 수 있습니다:
// tsc -m xxx
'commonjs' # NodeJS 모듈 정의
'amd' # AMD
'system' # SystemJS
'umd' # UMD
'es6' # ES Module
'es2015' # es6 와 동등
'esnext' # 아직 ES 규범에 수록되지 않은 최첨단 모듈 정의, 예를 들어 `import(), import.meta` 등
'none' # 모든 모듈 정의를 무효화, 예를 들어 import, export 등 (사용하면 오류)
기본 모듈 포맷은 CommonJS 또는 ES6 으로, --target 옵션과 관계있습니다 (target === "ES3" or "ES5" ? "CommonJS" : "ES6"). 만약 장래의 새 버전 ES 규범 중에서 모듈 정의에 변경이 있는 경우, 더욱 es2019, es2020... 등의 값이 추가되며, ES 규범의 각 버전 중의 모듈 정의에 대응합니다 (모듈 정의에 변경이 없는 경우는, 추가하지 않습니다)
P.S. 具体의 모듈 생성 예는, Code Generation for Modules 참조
--module 와 --target
--target(또는 -t) 옵션은 --module 과 매우 유사하며, 값은 다음과 같습니다:
// tsc -t xxx
'es3'
'es5'
'es2015'
'es2016'
'es2017'
'es2018'
'esnext'
생성되는 목표 코드가 어느 버전의 규범으로 정의된 언어 특성을 서포트하는지를 나타냅니다 (기본 ES3). --module 옵션과는 독립적입니다:
The module system is independent of the language implementation.
완전히 ES6 모듈 포맷을 만족하는 ES5 코드를 컴파일 생성할 수 있기 때문입니다. 예를 들어:
// tsconfig.json
"compilerOptions": {
"target": "es5",
"module": "es6"
}
게다가, 값도 --module 과는 다르며, 각 버전의 ES 규범은 1 개의 --target 具体값에 대응합니다. 각 버전마다 새로운 특성이 추가되기 때문입니다
P.S. 더 많은 관련 논의는, Understanding "target" and "module" in tsconfig 참조
P.S. 주의, --module 와 --target 는 모두생성되는 목표 코드를 대상으로 한 것으로, 소스 코드와는 무관계 입니다 (소스 코드는 완전히 ES 특성 전개해도 상관없습니다. 예를 들어 --lib 로 ES2016, es2017... 를 지정)
삼.모듈 인포트
일반적으로, import/require 는 목표 모듈 소스 코드를 인포트하고, 거기서 타입 정보를 추출합니다. 예를 들어:
// myModule.ts
export default {
name: 'my-module',
f() {
console.log('this is my module.');
}
}
// index.ts
import MyModule from './MyModule';
let m = MyModule;
// m 의 타입为 { name: string; f(): void; }
m.f();
(--module commonjs 하에서) index.ts 의 컴파일 결과는:
exports.__esModule = true;
var MyModule_1 = require("./MyModule");
var m = MyModule_1["default"];
// m 의 타입为 { name: string; f(): void; }
m.f();
온디맨드 로드
特殊的, 생성되는 목표 코드 중에서 인포트된 모듈이 사용되지 않은 경우 (예를 들어 타입 주석 중에서의만 사용), 컴파일 시에 모듈 참조가 자동으로 삭제됩니다:
// index.ts
import MyModule from './MyModule';
let m: typeof MyModule;
// 编译结果
exports.__esModule = true;
var m;
이비필수 참조를 제거 (reference-elision) 하는 특성은, 온디맨드 로드의 시나리오에서 특히 중요합니다:
// 타입을 인포트
import MyModule from './MyModule';
declare function require(moduleName: string): any;
let someCondition: boolean;
if (someCondition) {
let m: typeof MyModule = require('./MyModule');
// 동様に 올바른 타입을 가집니다
m.f();
}
// 编译结果
"use strict";
exports.__esModule = true;
var someCondition;
if (someCondition) {
var m = require('./MyModule');
// 동様に 올바른 타입을 가집니다
m.f();
}
사.모듈 타입 선언
타입이 부족한 서드파티 모듈에 대해, 선언 파일 (d.ts) 를 통해 타입 선언을 보충할 수 있습니다
구체적으로는, declare module 'my-module' {} 구문은 모듈을 선언할 수 있습니다 (import/require 가능):
// types.d.ts
declare module "my-module" {
function f(): string;
}
// index.ts
import { f } from "my-module";
const result: string = f();
이 방식을 통해 서드파티 모듈의 타입을 보충할 수 있지만, 만약 빠르게 사용하고 싶을 뿐 (수동으로 타입을 보충하기 싫은) 경우, 멤버 선언을 생략할 수 있습니다. 그 모든 멤버는 any 타입이 됩니다:
// types.d.ts
declare module "my-module";
// index.ts
import x, {y} from "my-module";
x(y);
와일드카드
特殊的, 일부 로딩 시스템은 비 JavaScript 콘텐츠의 인포트를 서포트합니다. 예를 들어 AMD :
define(['text!../templates/start.html'], function (template) {
//do something with the template text string.
});
此時모듈 와일드카드를 통해 그 타입을 정의할 수 있습니다:
// text! 로 시작하는 모든 모듈의 타입을 기술
declare module "text!*" {
const content: string;
export default content;
}
// !text 로 끝나는 모든 모듈의 타입을 기술
declare module "*!text" {
const content: string;
export default content;
}
이러한 특수한 모듈은 타입 정보를 가집니다:
import html from 'text!../templates/start.html';
// 正确
html.trim();
UMD 모듈
UMD 의 특징은 CommonJS 와 AMD 모듈 로딩과 호환성이 있으며, 글로벌에 노출하여 직접 사용도 할 수 있기 때문에, 그 모듈 선언도 비교적 특수합니다:
// math-lib.d.ts
export function isPrime(x: number): boolean;
export as namespace mathLib;
2 종류의 참조 방식:
// 직접 글로벌 변수를 통해 액세스
mathLib.isPrime(12);
// 모듈 인포트
import { isPrime } from './math-lib';
isPrime(122);
아직 댓글이 없습니다