前言
TypeScript 5.5 于 2024 年 6 月正式发布,带来了多项重要更新。本文基于 TypeScript 官方博客和发布说明,深入解析 5.5 版本的核心特性与实战应用。
一、Inferred Type Predicates(推断类型谓词)
1.1 问题背景
在 TypeScript 5.5 之前,使用Array.prototype.filter过滤数组时,类型推断不够智能:
constvalues=[1,2,null,3,undefined,4];// TypeScript 5.4 及之前constnumbers=values.filter(x=>x!==null&&x!==undefined);// 类型:(number | null | undefined)[]// 问题:TypeScript 无法推断出 null 和 undefined 已被过滤需要手动添加类型谓词:
functionisNotNullish<T>(value:T):valueisNonNullable<T>{returnvalue!==null&&value!==undefined;}constnumbers=values.filter(isNotNullish);// 类型:number[]1.2 TypeScript 5.5 的改进
现在 TypeScript 可以自动推断类型谓词:
constvalues=[1,2,null,3,undefined,4];constnumbers=values.filter(x=>x!==null&&x!==undefined);// 类型:number[] ✅// TypeScript 自动推断出这是一个类型谓词工作原理:
TypeScript 5.5 会分析函数体,识别以下模式:
x !== nullx !== undefinedtypeof x === "string"x instanceof SomeClass!!x(真值检查)
1.3 实战应用
1. 过滤 null/undefined
interfaceUser{id:number;name:string;email?:string;}constusers:(User|null)[]=[{id:1,name:"Alice",email:"alice@example.com"},null,{id:2,name:"Bob"},null,];// TypeScript 5.5 自动推断constvalidUsers=users.filter(user=>user!==null);// 类型:User[] ✅constusersWithEmail=validUsers.filter(user=>user.email!==undefined);// 类型:User[] (email 仍然是可选的)2. 类型守卫
typeShape=|{kind:"circle";radius:number}|{kind:"square";size:number}|{kind:"rectangle";width:number;height:number};constshapes:Shape[]=[{kind:"circle",radius:10},{kind:"square",size:20},{kind:"rectangle",width:30,height:40},];// 自动推断类型谓词constcircles=shapes.filter(shape=>shape.kind==="circle");// 类型:{ kind: "circle"; radius: number }[] ✅3. 真值检查
constmixedArray=[0,1,"","hello",false,true,null,undefined];consttruthyValues=mixedArray.filter(x=>!!x);// 类型:(string | number | boolean)[] ✅// 自动过滤了 null、undefined、0、""、false4. instanceof 检查
classDog{bark(){console.log("Woof!");}}classCat{meow(){console.log("Meow!");}}constanimals:(Dog|Cat)[]=[newDog(),newCat(),newDog()];constdogs=animals.filter(animal=>animalinstanceofDog);// 类型:Dog[] ✅dogs.forEach(dog=>dog.bark());// 类型安全1.4 限制与注意事项
1. 仅支持简单表达式
// ✅ 支持constnumbers=values.filter(x=>x!==null);// ❌ 不支持复杂逻辑constnumbers=values.filter(x=>{constisValid=x!==null&&x!==undefined;returnisValid;});// 类型:(number | null | undefined)[]2. 需要明确的类型检查
// ✅ 支持conststrings=values.filter(x=>typeofx==="string");// ❌ 不支持自定义函数functionisString(x:unknown){returntypeofx==="string";}conststrings=values.filter(isString);// 需要手动添加类型谓词二、Control Flow Narrowing for Constant Indexed Accesses(常量索引访问的控制流收窄)
2.1 问题背景
在 TypeScript 5.4 及之前,对象属性的类型收窄不够智能:
functionprocessUser(user:{name:string|null}){if(user.name!==null){console.log(user.name.toUpperCase());// ✅ 这里 user.name 被收窄为 string}constkey="name"asconst;if(user[key]!==null){console.log(user[key].toUpperCase());// ❌ TypeScript 5.4: 错误!user[key] 仍然是 string | null}}2.2 TypeScript 5.5 的改进
现在常量索引访问也支持类型收窄:
functionprocessUser(user:{name:string|null}){constkey="name"asconst;if(user[key]!==null){console.log(user[key].toUpperCase());// ✅ TypeScript 5.5: user[key] 被收窄为 string}}2.3 实战应用
1. 动态属性访问
interfaceConfig{apiUrl:string|null;timeout:number|null;retries:number|null;}functiongetConfigValue<KextendskeyofConfig>(config:Config,key:K):NonNullable<Config[K]>|null{constvalue=config[key];if(value!==null){returnvalue;// ✅ 类型正确}returnnull;}2. 表单验证
interfaceFormData{email:string|null;password:string|null;confirmPassword:string|null;}functionvalidateForm(data:FormData):boolean{constrequiredFields=["email","password","confirmPassword"]asconst;for(constfieldofrequiredFields){if(data[field]===null||data[field].trim()===""){// ✅ TypeScript 5.5: data[field] 被正确收窄console.error(`${field}is required`);returnfalse;}}returntrue;}3. 状态管理
interfaceState{user:{id:number;name:string}|null;posts:Array<{id:number;title:string}>|null;loading:boolean;}functionselectData<KextendskeyofState>(state:State,key:K):NonNullable<State[K]>{constvalue=state[key];if(value===null){thrownewError(`${String(key)}is not loaded`);}returnvalue;// ✅ 类型正确}三、JSDoc @import 标签
3.1 问题背景
在 JavaScript 文件中使用 JSDoc 类型注解时,导入类型很麻烦:
// 之前的方式/** * @typedef {import('./types').User} User * @param {User} user */functiongreetUser(user){console.log(`Hello,${user.name}`);}3.2 TypeScript 5.5 的改进
新增@import标签,语法更简洁:
/** @import { User } from './types' *//** * @param {User} user */functiongreetUser(user){console.log(`Hello,${user.name}`);}3.3 实战应用
1. 导入多个类型
/** @import { User, Post, Comment } from './types' *//** * @param {User} user * @param {Post[]} posts * @param {Comment[]} comments */functionrenderUserProfile(user,posts,comments){// 实现}2. 命名空间导入
/** @import * as Types from './types' *//** * @param {Types.User} user */functionprocessUser(user){// 实现}3. 默认导入
/** @import Config from './config' *//** * @param {Config} config */functioninitialize(config){// 实现}四、正则表达式语法检查
4.1 新特性
TypeScript 5.5 增强了对正则表达式的静态检查:
// ❌ 错误:无效的正则表达式constregex1=/[/;// Error: Unterminated character class// ❌ 错误:无效的转义序列constregex2=/\k/;// Error: Invalid escape sequence// ❌ 错误:无效的命名捕获组引用constregex3=/(?<name>\w+) \k<nam>/;// Error: Invalid named capture reference4.2 实战应用
1. 邮箱验证
// ✅ 正确的正则表达式constemailRegex=/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;functionvalidateEmail(email:string):boolean{returnemailRegex.test(email);}2. URL 解析
// ✅ 使用命名捕获组consturlRegex=/^(?<protocol>https?):\/\/(?<domain>[^\/]+)(?<path>\/.*)?$/;functionparseUrl(url:string){constmatch=url.match(urlRegex);if(match&&match.groups){return{protocol:match.groups.protocol,domain:match.groups.domain,path:match.groups.path||"/",};}returnnull;}五、性能优化
5.1 单态化(Monomorphization)
TypeScript 5.5 改进了泛型函数的性能:
// TypeScript 会为不同的类型参数生成优化的代码functionidentity<T>(value:T):T{returnvalue;}constnum=identity(42);// 优化为数字版本conststr=identity("hello");// 优化为字符串版本5.2 编译速度提升
- 类型检查速度提升 10-15%
- 增量编译更快
- 内存使用优化
六、其他改进
6.1 编辑器支持
1. 更好的自动导入
// 输入 "User",自动建议导入import{User}from'./types';2. 改进的重构工具
- 更智能的重命名
- 更准确的查找引用
- 更好的代码导航
6.2 配置选项
1.verbatimModuleSyntax改进
// tsconfig.json{"compilerOptions":{"verbatimModuleSyntax":true}}更严格的模块语法检查,避免运行时错误。
七、迁移指南
7.1 升级步骤
# 1. 更新 TypeScriptnpminstall-Dtypescript@5.5# 2. 更新类型定义npminstall-D@types/node@latest# 3. 检查编译npx tsc--noEmit7.2 潜在的破坏性变更
1. 更严格的类型检查
// 可能需要修复的代码constvalues=[1,2,null,3];constnumbers=values.filter(x=>x);// 现在类型更精确2. 正则表达式错误
// 需要修复无效的正则表达式constregex=/[/;// ❌ 现在会报错八、最佳实践
8.1 利用推断类型谓词
// ✅ 好:让 TypeScript 自动推断constvalidUsers=users.filter(user=>user!==null);// ❌ 不必要:手动添加类型谓词(除非逻辑复杂)constvalidUsers=users.filter((user):userisUser=>user!==null);8.2 使用 @import 简化 JSDoc
// ✅ 好:使用 @import/** @import { User } from './types' */// ❌ 旧方式:使用 @typedef/** @typedef {import('./types').User} User */8.3 验证正则表达式
// ✅ 好:在开发时就发现错误constregex=/^[a-z]+$/;// ❌ 坏:运行时才发现错误constregex=newRegExp("^[a-z+$");// 语法错误九、总结
TypeScript 5.5 带来了多项重要改进:
核心特性:
✅ 推断类型谓词 - 更智能的类型收窄
✅ 常量索引访问收窄 - 更好的对象属性类型推断
✅ JSDoc @import - 更简洁的类型导入
✅ 正则表达式检查 - 编译时发现错误
✅ 性能优化 - 更快的编译速度
升级建议:
- 新项目直接使用 5.5
- 现有项目逐步升级
- 注意潜在的破坏性变更
- 利用新特性简化代码
TypeScript 5.5 让类型系统更智能、更易用,值得升级!