8 điểm bởi GN⁺ 28 ngày trước | 3 bình luận | Chia sẻ qua WhatsApp
  • Đây là bản phát hành cuối cùng dựa trên codebase JavaScript hiện tại, đồng thời là bản phát hành cầu nối để chuẩn bị cho quá trình chuyển sang TypeScript 7.0, bản port native viết bằng Go
  • Bao gồm các cải tiến về suy luận kiểu và phân giải mô-đun như nới lỏng tính nhạy ngữ cảnh đối với các hàm không dùng this, hỗ trợ subpath imports bắt đầu bằng #/
  • Hiện đại hóa mạnh mẽ các giá trị mặc định của tùy chọn trình biên dịch như chuyển mặc định strict sang true, mặc định target thành es2025, mặc định types thành []
  • Ngừng hỗ trợ hàng loạt tùy chọn legacy như target ES5, mô-đun AMD/UMD/SystemJS, --baseUrl, --moduleResolution node10
  • Bổ sung hỗ trợ kiểu cho các đề xuất ECMAScript Stage 4 mới nhất như Temporal API, getOrInsert/getOrInsertComputed của Map, RegExp.escape

Vị trí của TypeScript 6.0

  • Đây là bản phát hành cuối cùng dựa trên codebase JavaScript hiện tại, đóng vai trò cầu nối cho quá trình chuyển sang TypeScript 7.0 (bản port native bằng Go)
  • TypeScript 7.0 tận dụng mã native và đa luồng với bộ nhớ dùng chung, và hiện đã rất gần trạng thái hoàn thiện
  • Phần lớn thay đổi trong 6.0 nhằm đồng bộ và chuẩn bị cho việc tiếp nhận 7.0
  • Có thể trải nghiệm sớm TypeScript 7.0 qua tiện ích mở rộng VS Code hoặc gói npm

Các thay đổi sau Beta và RC

  • Điều chỉnh kiểm tra kiểu của biểu thức hàm trong các lời gọi generic, đặc biệt là biểu thức JSX generic — giúp bắt được nhiều lỗi hơn trong code hiện có, nhưng một số lời gọi generic có thể sẽ cần truyền đối số kiểu tường minh
  • Mở rộng việc ngừng hỗ trợ cú pháp import assertion (assert) sang cả lời gọi import()
  • Cập nhật kiểu DOM — phản ánh các tiêu chuẩn web mới nhất, bao gồm cả điều chỉnh liên quan đến Temporal API

Nới lỏng tính nhạy ngữ cảnh của các hàm không dùng this

  • Khi suy luận kiểu, TypeScript phân loại các hàm có tham số không có kiểu tường minh là hàm nhạy ngữ cảnh (contextually sensitive function), và xử lý chúng ở thứ tự ưu tiên thấp hơn trong quá trình suy luận
  • Các hàm viết bằng cú pháp method có tham số this ngầm, nên khác với arrow function, trước đây luôn bị xem là nhạy ngữ cảnh
    • Điều này khiến suy luận kiểu đôi khi thất bại tùy theo thứ tự method trong object literal
  • Trong TypeScript 6.0, các hàm thực sự không sử dụng this sẽ không còn bị xem là nhạy ngữ cảnh
    • Các hàm này được ưu tiên cao hơn trong suy luận kiểu, nên có thể suy luận đúng bất kể thứ tự method
  • Được triển khai nhờ đóng góp của Mateusz Burzyński

Hỗ trợ Subpath Imports bắt đầu bằng #/

  • Tính năng subpath imports của Node.js cho phép định nghĩa bí danh cho các mô-đun nội bộ gói bằng trường imports trong package.json
  • Trước đây, sau # bắt buộc phải có ký tự nên không thể dùng đường dẫn bắt đầu bằng #/
    • Điều này gây nhầm lẫn cho các lập trình viên đã quen với quy ước tiền tố @/ trong bundler
  • Gần đây Node.js đã bắt đầu hỗ trợ subpath imports bắt đầu bằng #/
    • Cho phép viết ánh xạ ngắn gọn như "#/*": "./dist/*"
  • TypeScript 6.0 hỗ trợ điều này với các tùy chọn --moduleResolution nodenextbundler
  • Được triển khai nhờ đóng góp của magic-akari

Cho phép kết hợp --moduleResolution bundler với --module commonjs

  • Trước đây --moduleResolution bundler chỉ dùng được với --module esnext hoặc --module preserve
  • Do --moduleResolution node (node10) bị ngừng hỗ trợ, tổ hợp mới này là lộ trình nâng cấp phù hợp nhất cho nhiều dự án
  • Về lâu dài, vẫn khuyến nghị migrate sang --module preserve + --moduleResolution bundler hoặc --module nodenext

Cờ --stableTypeOrdering

  • Type ID được gán cho các kiểu trong nội bộ TypeScript phụ thuộc vào thứ tự xử lý, và union type được sắp xếp dựa trên đó
    • Điều này có thể gây ra hiện tượng khó đoán khi kết quả emit khai báo thay đổi theo thứ tự khai báo
  • TypeScript 7.0 sẽ đưa vào kiểm tra kiểu song song, nên để giải quyết vấn đề gán ID không quyết định, nó dùng thuật toán sắp xếp quyết định dựa trên nội dung
    • Ví dụ: 100 | 500 sẽ luôn được in ra theo cùng một thứ tự
  • Khi bật cờ --stableTypeOrdering trong 6.0, có thể đồng bộ hành vi sắp xếp kiểu với 7.0 để giảm khác biệt giữa hai codebase
    • Có thể làm giảm hiệu năng kiểm tra kiểu tới 25%
    • Nếu phát sinh lỗi kiểu do khác biệt suy luận, có thể khắc phục bằng cách thêm đối số kiểu tường minh hoặc annotation cho biến
  • Đây là cờ chỉ nhằm chẩn đoán migration từ 6.0 lên 7.0, không khuyến nghị dùng lâu dài

Tùy chọn es2025 (targetlib)

  • ES2025 không có tính năng ngôn ngữ JavaScript mới, nhưng có bổ sung kiểu cho các API built-in như RegExp.escape
  • Promise.try, các phương thức của Iterator, các phương thức của Set vốn nằm trong esnext nay đã được chuyển sang es2025
  • Được triển khai nhờ đóng góp của Kenta Moriuchi

Hỗ trợ kiểu cho Temporal API

  • TypeScript 6.0 đã bao gồm kiểu built-in cho đề xuất Temporal đã đạt Stage 4
  • Có thể dùng với --target esnext hoặc "lib": ["esnext"] (hoặc esnext.temporal chi tiết hơn)
  • Có thể sử dụng an toàn về kiểu với các API như Temporal.Now.instant().subtract(), .add()
  • Đã có mặt trong nhiều runtime và với Stage 4, đây là một phần chính thức của ngôn ngữ JavaScript
  • Được triển khai nhờ đóng góp của Renegade334

Hỗ trợ kiểu cho các phương thức "upsert" của Map (getOrInsert / getOrInsertComputed)

  • Giúp đơn giản hóa mẫu lặp lại thường gặp là kiểm tra key trong Map rồi thiết lập giá trị mặc định nếu chưa có
  • Đề xuất "upsert" của ECMAScript đã đạt Stage 4, bổ sung hai phương thức mới cho MapWeakMap
    • getOrInsert: nếu key chưa có thì chèn giá trị mặc định được chỉ định rồi trả về
    • getOrInsertComputed: khi chi phí tạo giá trị mặc định lớn, có thể tính toán trì hoãn qua callback
      • Callback nhận key làm đối số, nên cũng có thể dùng để tạo giá trị mặc định dựa trên key
  • Đã được thêm vào lib esnext, có thể dùng ngay trong TypeScript 6.0
  • Được triển khai nhờ đóng góp của Renegade334

RegExp.escape

  • Hàm RegExp.escape để escape các ký tự đặc biệt trong biểu thức chính quy đã đạt Stage 4
  • Có trong lib es2025, nên dùng được trong TypeScript 6.0
  • Được triển khai nhờ đóng góp của Kenta Moriuchi

Tích hợp dom.iterabledom.asynciterable vào lib dom

  • Trước đây, để dùng iteration trên NodeList, HTMLCollection... cần khai báo "lib": ["dom", "dom.iterable"]
  • Trong TypeScript 6.0, nội dung của lib.dom.iterable.d.tslib.dom.asynciterable.d.ts đã được tích hợp hoàn toàn vào lib.dom.d.ts
    • dom.iterable, dom.asynciterable vẫn có thể được tham chiếu nhưng là các file rỗng
  • Vì tất cả các trình duyệt hiện đại lớn đều đã hỗ trợ các tính năng này, đây là cải tiến tiện dụng nhằm loại bỏ một điểm dễ gây nhầm lẫn phổ biến

Các thay đổi lớn về giá trị mặc định

  • strict mặc định là true: vì đa số dự án mới đều muốn bật strict mode, các dự án trước đây dựa vào mặc định false sẽ cần đặt rõ "strict": false
  • module mặc định là esnext: phản ánh thực tế ESM đã trở thành định dạng mô-đun chủ đạo
  • target mặc định là phiên bản ES mới nhất (hiện là es2025): do runtime evergreen đã phổ biến, không còn cần transpile xuống phiên bản cũ
  • noUncheckedSideEffectImports mặc định là true: giúp phát hiện lỗi gõ sai trong các import chỉ nhằm side effect
  • libReplacement mặc định là false: cải thiện hiệu năng mặc định bằng cách tránh lỗi phân giải mô-đun không cần thiết và giảm số mục bị theo dõi

rootDir đổi mặc định thành .

  • Trước đây, nếu không chỉ định thì giá trị này được xác định bằng cách suy luận thư mục chung của mọi file input không phải khai báo
    • Điều đó gây ra vấn đề là muốn biết một file có thuộc dự án hay không thì phải load và parse dự án đó
  • Trong TypeScript 6.0, mặc định được cố định là thư mục chứa tsconfig.json
  • Nếu file nguồn nằm sâu hơn tsconfig.json, cần chỉ định tường minh như "rootDir": "./src"
    • Nếu không đặt, có thể sinh ra cấu trúc output ngoài ý muốn như ./dist/src/index.js

types đổi mặc định thành []

  • Trước đây, mọi gói trong node_modules/@types đều được tự động đưa vào, tạo ra overhead lớn cho thời gian build
    • Trong các repository thông thường, thường có hàng trăm gói @types được đưa vào gián tiếp
  • Trong TypeScript 6.0, mặc định được đổi thành [] (mảng rỗng) để tránh tải các file khai báo không cần thiết
  • Đã ghi nhận các trường hợp thời gian build cải thiện 20–50%
  • Phần lớn dự án sẽ cần cấu hình rõ như "types": ["node"] hoặc "types": ["node", "jest"]
    • Có thể khôi phục hành vi cũ bằng "types": ["*"]

Các mục ngừng hỗ trợ (Deprecation)

target: es5 bị ngừng hỗ trợ

  • Target ES5 gần như không còn trường hợp sử dụng vì IE đã bị khai tử và trình duyệt evergreen đã phổ biến
  • Target tối thiểu được nâng lên ES2015, nếu cần output ES5 thì nên dùng trình biên dịch bên ngoài

--downlevelIteration bị ngừng hỗ trợ

  • Tùy chọn này chỉ có tác dụng với emit ES5, nên mất mục đích khi ES5 bị ngừng hỗ trợ

--moduleResolution node (node10) bị ngừng hỗ trợ

  • Phản ánh thuật toán phân giải mô-đun của Node.js 10, không còn khớp với hành vi của Node.js hiện đại
  • Khuyến nghị migrate sang nodenext (target trực tiếp Node.js) hoặc bundler (dùng bundler/Bun)

Các giá trị mô-đun AMD, UMD, SystemJS bị ngừng hỗ trợ

  • --module amd, --module umd, --module systemjs, --module none đều không còn được hỗ trợ
  • Vì ESM đã được hỗ trợ rộng rãi trong cả trình duyệt lẫn Node.js, cần chuyển sang bundler hoặc target ESM

--baseUrl bị ngừng hỗ trợ

  • Dù chủ yếu được dùng làm tiền tố cho paths, nó cũng hoạt động như gốc tra cứu trong phân giải mô-đun, dễ gây ra các vấn đề phân giải đường dẫn ngoài ý muốn
  • Có thể migrate bằng cách bỏ baseUrl và thêm trực tiếp tiền tố vào từng mục paths
    • Ví dụ: "@app/*": ["app/*"]"@app/*": ["./src/app/*"]

--moduleResolution classic bị ngừng hỗ trợ

  • Đây là thuật toán phân giải mô-đun ban đầu của TypeScript, hiện mọi trường hợp thực tế đều có thể thay bằng nodenext hoặc bundler

esModuleInterop falseallowSyntheticDefaultImports false bị ngừng hỗ trợ

  • Không thể đặt hai tùy chọn này là false nữa, nên hành vi interop an toàn luôn được bật
  • Cần điều chỉnh như import * as express from "express"import express from "express"

--alwaysStrict false bị ngừng hỗ trợ

  • Mọi mã đều được xem là chạy trong JavaScript strict mode, nên những mã dùng await, static, private... như định danh thông thường sẽ cần đổi tên

outFile bị ngừng hỗ trợ

  • Đây là tính năng gộp nhiều file input thành một, nhưng nay đã được các bundler bên ngoài thay thế như Webpack, Rollup, esbuild, Vite
  • Quyết định này nhằm giúp TypeScript tập trung vào vai trò cốt lõi là kiểm tra kiểu và emit khai báo

Cú pháp module legacy (khai báo namespace) bị ngừng hỗ trợ

  • Cú pháp module Foo { ... } bị ngừng hỗ trợ hoàn toàn, cần dùng namespace Foo { ... }
  • Khai báo ambient module dạng declare module "some-module" { ... } vẫn tiếp tục được hỗ trợ
  • Mục đích là tránh xung đột với đề xuất module block của ECMAScript

Từ khóa import asserts bị ngừng hỗ trợ

  • Cần đổi từ import ... asserts { type: "json" } sang import ... with { type: "json" }
  • Đây là thay đổi theo việc đề xuất import assertions được chuyển thành đề xuất import attributes (with)

Directive no-default-lib bị ngừng hỗ trợ

  • /// <reference no-default-lib="true"/> không còn được hỗ trợ, nên dùng --noLib hoặc --libReplacement

Lỗi khi chỉ định file dòng lệnh nếu có tsconfig.json

  • Khi chạy tsc foo.ts, nếu cùng thư mục có tsconfig.json thì sẽ phát sinh lỗi
  • Có thể bỏ qua tường minh bằng cờ --ignoreConfig

Chuẩn bị cho TypeScript 7.0

  • Các tùy chọn bị ngừng hỗ trợ trong 6.0 vẫn có thể tiếp tục dùng không lỗi bằng cấu hình "ignoreDeprecations": "6.0", nhưng sẽ bị loại bỏ hoàn toàn trong 7.0
  • Có thể dùng công cụ ts5to6 để tự động điều chỉnh như baseUrl, rootDir
  • TypeScript 7.0 dự kiến phát hành trong vài tháng tới và đã được tiếp nhận rộng rãi trong các codebase lớn cả trong lẫn ngoài Microsoft
  • Khuyến khích dùng bản dựng nightly native previewtiện ích mở rộng VS Code để gửi phản hồi

3 bình luận

 

Mình rất mong chờ thời điểm chuyển hoàn toàn sang trình biên dịch dựa trên Go!

 

Hả? Sau này TypeScript sẽ chuyển sang native dựa trên Go à?

 

Chỉ compiler thôi