Khám phá nội dung biên dịch của TypeScript
Trình biên dịch TypeScript, thường được gọi là tsc
, là một trong những thành phần cốt lõi của hệ sinh thái TypeScript. Nó chuyển đổi mã TypeScript thành JavaScript trong khi thực thi các quy tắc gõ tĩnh. Trong bài viết này, chúng ta sẽ đi sâu vào hoạt động bên trong của trình biên dịch TypeScript để hiểu rõ hơn về cách nó xử lý và chuyển đổi mã TypeScript.
1. Quá trình biên dịch TypeScript
Trình biên dịch TypeScript thực hiện một loạt các bước để chuyển đổi TypeScript thành JavaScript. Sau đây là tổng quan cấp cao về quy trình:
- Phân tích cú pháp các tệp nguồn thành Cây cú pháp trừu tượng (AST).
- Liên kết và kiểm tra kiểu AST.
- Phát ra mã JavaScript và khai báo đầu ra.
Hãy cùng khám phá các bước này chi tiết hơn.
2. Phân tích mã TypeScript
Bước đầu tiên trong quá trình biên dịch là phân tích cú pháp mã TypeScript. Trình biên dịch lấy các tệp nguồn, phân tích chúng thành AST và thực hiện phân tích từ vựng.
Sau đây là cách đơn giản hóa cách bạn có thể truy cập và thao tác AST bằng API nội bộ của TypeScript:
import * as ts from 'typescript';
const sourceCode = 'let x: number = 10;';
const sourceFile = ts.createSourceFile('example.ts', sourceCode, ts.ScriptTarget.Latest);
console.log(sourceFile);
Hàm createSourceFile
được sử dụng để chuyển đổi mã TypeScript thô thành AST. Đối tượng sourceFile
chứa cấu trúc đã phân tích cú pháp của mã.
3. Liên kết và Kiểm tra Kiểu
Sau khi phân tích cú pháp, bước tiếp theo là liên kết các ký hiệu trong AST và thực hiện kiểm tra kiểu. Giai đoạn này đảm bảo rằng tất cả các định danh được liên kết với các khai báo tương ứng của chúng và kiểm tra xem mã có tuân theo các quy tắc kiểu của TypeScript hay không.
Kiểm tra kiểu được thực hiện bằng cách sử dụng lớp TypeChecker
. Sau đây là ví dụ về cách tạo chương trình và lấy thông tin kiểu:
const program = ts.createProgram(['example.ts'], {});
const checker = program.getTypeChecker();
// Get type information for a specific node in the AST
sourceFile.forEachChild(node => {
if (ts.isVariableStatement(node)) {
const type = checker.getTypeAtLocation(node.declarationList.declarations[0]);
console.log(checker.typeToString(type));
}
});
Trong ví dụ này, TypeChecker
sẽ kiểm tra kiểu khai báo biến và lấy thông tin kiểu từ AST.
4. Mã phát thải
Sau khi kiểm tra kiểu hoàn tất, trình biên dịch sẽ tiến hành giai đoạn phát. Đây là nơi mã TypeScript được chuyển đổi thành JavaScript. Đầu ra cũng có thể bao gồm các tệp khai báo và bản đồ nguồn, tùy thuộc vào cấu hình.
Sau đây là một ví dụ đơn giản về cách sử dụng trình biên dịch để phát ra mã JavaScript:
const { emitSkipped, diagnostics } = program.emit();
if (emitSkipped) {
console.error('Emission failed:');
diagnostics.forEach(diagnostic => {
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
console.error(message);
});
} else {
console.log('Emission successful.');
}
Hàm program.emit
tạo ra đầu ra JavaScript. Nếu có bất kỳ lỗi nào trong quá trình phát, chúng sẽ được ghi lại và hiển thị.
5. Tin nhắn chẩn đoán
Một trong những trách nhiệm chính của trình biên dịch TypeScript là cung cấp các thông báo chẩn đoán có ý nghĩa cho nhà phát triển. Các thông báo này được tạo ra trong cả giai đoạn kiểm tra kiểu và phát hành mã. Chẩn đoán có thể bao gồm các cảnh báo và lỗi, giúp nhà phát triển nhanh chóng xác định và giải quyết các vấn đề.
Sau đây là cách lấy và hiển thị chẩn đoán từ trình biên dịch:
const diagnostics = ts.getPreEmitDiagnostics(program);
diagnostics.forEach(diagnostic => {
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
console.log(`Error ${diagnostic.code}: ${message}`);
});
Trong ví dụ này, chẩn đoán được trích xuất từ chương trình và in ra bảng điều khiển.
6. Chuyển đổi TypeScript với API biên dịch
API trình biên dịch TypeScript cho phép các nhà phát triển tạo các chuyển đổi tùy chỉnh. Bạn có thể sửa đổi AST trước khi phát hành mã, cho phép tùy chỉnh mạnh mẽ và các công cụ tạo mã.
Sau đây là ví dụ về một phép chuyển đổi đơn giản đổi tên tất cả các biến thành newVar
:
const transformer = (context: ts.TransformationContext) => {
return (rootNode: T) => {
function visit(node: ts.Node): ts.Node {
if (ts.isVariableDeclaration(node)) {
return ts.factory.updateVariableDeclaration(
node,
ts.factory.createIdentifier('newVar'),
node.type,
node.initializer
);
}
return ts.visitEachChild(node, visit, context);
}
return ts.visitNode(rootNode, visit);
};
};
const result = ts.transform(sourceFile, [transformer]);
console.log(result.transformed[0]);
Chuyển đổi này sẽ truy cập vào từng nút trong AST và đổi tên các biến khi cần thiết.
Phần kết luận
Khám phá nội bộ trình biên dịch TypeScript giúp hiểu sâu hơn về cách xử lý và chuyển đổi mã TypeScript. Cho dù bạn đang muốn xây dựng các công cụ tùy chỉnh hay cải thiện kiến thức về cách TypeScript hoạt động, việc đào sâu vào nội bộ trình biên dịch có thể là một trải nghiệm khai sáng.