首页 / 文章 / TypeScript 6的悄然成就与资深开发者的JavaScript使用习惯

TypeScript 6的悄然成就与资深开发者的JavaScript使用习惯

了解 TypeScript 6 中那些常被忽视的功能,如显式的资源管理机制和 const 类型参数,以及高级工程师们日常工作中常用的 JavaScript 习惯用法。

1228 词

现代 JavaScript 和 TypeScript 的发展远不止那些备受瞩目的新功能或更新日志中的条目。目前有两类重要的实践方向:一是 TypeScript 6 中那些较为低调、宣传不多的新增功能,二是那些能让高级开发者提交的 pull request 区别于普通功能性代码的 JavaScript 习惯用法。其实这两者都关乎同一项素养——充分了解各种工具,以便在需要时选用最合适的而非最熟悉的工具。本文将依次探讨这两方面,首先介绍 TypeScript 6 中那些鲜为人知的新功能,然后再讲一讲经验丰富的工程师们通常会使用的 JavaScript 模式。

值得采用的低调 TypeScript 6 升级

关于 TypeScript 6 的大多数报道都只提到严格模式和 ES2025 现已成为默认设置这一事实。这确实是个重大变化,但却掩盖了一组更小的改进,这些改进悄然消除了长期存在的变通方法。如果你只阅读 tsconfig-default 的标题,就会错过该版本中更有价值的内容。

无需刻意记忆的自动清理功能

常见的错误来源之一就是忘记关闭数据库连接或移除监听器。TypeScript 6 通过明确的资源管理机制解决了这一问题,允许你标记某个值,使其在离开作用域后自动被清理。

function readUserSession() {
  using session = openSession(); // auto-disposed at scope end
  const user = session.getUser();
  return user.name;
} // session.dispose() called automatically here

这无需再使用原本仅用于确保资源清理的 try/finally 块。虽然语法上的新增内容不多,但对于涉及文件、套接字或开放连接的任何操作而言,其带来的可靠性提升却十分显著。

无需到处使用 as const 的字面量类型

此前,若要通过泛型函数保持字面量类型的完整性,就必须在代码中随处使用 as const。TypeScript 6 引入了常量类型参数,能够自动保留字面量的推断结果。

function createConfig<const T extends Record<string, unknown>>(config: T) {
  return config;
}

const config = createConfig({
  env: "production",
  features: ["auth", "billing"],
});
// config.env is "production", not string
// config.features is readonly ["auth", "billing"], not string[]

如果你在构建带类型的 API 客户端或编写 Redux action creator,这将大幅减少原本需要的大量 as const 代码。

switch (true) 中的正确类型缩小

另一个值得欢迎的改进是:现在可以在 switch (true) 语句中正确地缩小区分联合类型的范围,而过去为了让编译器理解类型,不得不使用嵌套的 if/else 结构。

function area(shape: Shape): number {
  switch (true) {
    case shape.kind === "circle":
      return Math.PI * shape.radius ** 2; // narrowed to Circle
    case shape.kind === "rectangle":
      return shape.width * shape.height; // narrowed to Rectangle
  }
}

通过这一变更,您无需为了保持类型收窄功能而放弃使用 switch 语句。

Temporal 获得了一等类型支持

TypeScript 6 还为 Temporal API 提供了内置类型,因此您不再需要通过手动类型断言来应对 Date 及其时区相关的复杂问题。

const meetingStart = Temporal.Instant.from("2026-04-06T10:00:00Z");
const localTime = meetingStart.toZonedDateTimeISO("America/Toronto");

真正能正常解析的子路径导入

另一个虽不那么严重但确实令人烦恼的问题也得到了解决:现在内部子路径导入能够正确解析,无需依赖一长串相对路径段。

import { User } from "#/models/user.js";
// instead of ../../../models/user.js 🙃

TypeScript的产品经理Daniel Rosenwasser将TS6描述为通往即将推出的基于Go语言的TypeScript 7的过渡桥梁,这表明这些并非临时性的实验性功能——它们将会长期保留。如果您的团队使用全栈JavaScript、React或Next.js框架进行开发,现在就养成这些习惯,就能在TypeScript 7到来时减少意外情况。总体而言,TypeScript 6不仅带来了新的编译器默认设置,还实现了更高效的资源管理、更精准的类型推断,以及更少的手动解决方案。在下一个拉取请求中尝试使用using和const类型参数,您可能会惊讶于之前竟没有它们也能应付自如。

让资深开发者脱颖而出的JavaScript开发习惯

了解 TypeScript 的新功能只是其中一半。另一半在于日常如何编写 JavaScript 代码。当某位资深工程师的 pull request 比其他人的更简洁清晰时,这往往并非因为其聪明才智,而是因为他们能毫不犹豫地运用一套始终如一的习惯。

可选链与空值合并运算符

相比 user && user.profile && user.profile.name 这样的链式写法,资深开发者更倾向于使用可选链与空值合并运算符的组合。

const displayName = user?.profile?.name ?? 'Guest';

这种写法简洁明了,能够避免因缺失值导致的问题,而且当周围的代码被重构时也不会出错。

带默认值的解构赋值

为属性、API 返回值以及函数参数设置合理的默认值并进行解构赋值,对这类资深开发者来说已是自然而然的操作。

function createUser({ name, role = 'member', isActive = true }) {
  return { name, role, isActive };
}

这样做可以直接在函数签名中明确说明数据的预期结构。

使用 Async/Await 而非嵌套的 Promise

虽然仍然可以使用 .then().then().catch() 的嵌套写法,但随着逻辑复杂度的增加,其可扩展性较差。相比之下,使用 async/await 的方式更为合适:

async function fetchOrders(userId) {
  try {
    const res = await fetch(`/api/orders/${userId}`);
    if (!res.ok) throw new Error('Failed to fetch orders');
    return await res.json();
  } catch (err) {
    console.error('Order fetch failed:', err);
    throw err;
  }
}

结果从上到下依次呈现,便于调试和测试。

使用数组方法而非手动循环

通过使用 mapfilterreducefind 方法,可以替代大多数手写的 for 循环。

const activeAdmins = users
  .filter((u) => u.isActive && u.role === 'admin')
  .map((u) => u.email);

这样就能减少可变变量的使用,同时降低因索引错误导致的故障。

使用工具类型而非重复的接口

与其重新定义几乎相同的接口,成熟的 TypeScript 代码更倾向于使用 PartialPickOmit 等工具类型。

type UserFormInput = Pick<User, 'name' | 'email'> & Partial<Pick<User, 'phone'>>;

这样能够保持单一的真相来源,因此架构变更时只需在更少的地方进行更新。

React 中的钩子与组合

在成熟的 React 代码库中,经常可以看到用于提取并封装逻辑的自定义钩子。

function useDebouncedValue(value, delay = 300) {
  const [debounced, setDebounced] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(timer);
  }, [value, delay]);

  return debounced;
}

这样的钩子可以让底层逻辑被重复使用,并且能够独立于任何特定组件进行测试。

结构化状态管理

在状态管理方面,许多资深团队更倾向于使用 Redux Toolkit 的 slices,而非通过 switch 语句编写的自定义 reducer,因为 RTK 能减少样板代码,并通过 Immer 自动实现不可变性。

正如道格拉斯·克罗克福德所言,JavaScript由“优点与缺点”构成——正是这种灵活性让该语言如此强大,同时也容易引发混淆。懂得该依赖哪些部分,才能区分出经验丰富的工程师与仍在摸索中的初学者。

有一组简短的原则可以将这一切联系起来:优先考虑可读性而非巧妙设计,让TypeScript的类型系统承担文档说明的任务而非依赖注释,以及通过小型组合函数而非大型单体函数来实现功能。

相关阅读