JavaScript 语义与 TypeScript 约束
从名称、对象和函数进入类型擦除、收窄、结构兼容、判别联合和外部数据校验。
名称、对象与函数
名称绑定到值。对象值可以被多个名称引用;改变对象成员后,其他引用能观察这个变化,重新给一个名称赋值则改变该名称的绑定。const 禁止重新赋值,仍允许对象内部按自身规则改变。是否可变由对象和操作决定。
const first = { count: 1 };
const second = first;
second.count = 2;
// first.count 与 second.count 均为 2。函数在调用时创建局部执行环境。闭包让函数保留对外层词法环境的访问,随后读取其中的当前绑定。事件回调与异步调用因而需要分辨创建、捕获和执行时机。不同循环声明、框架快照和对象引用会改变结果,不能把所有闭包简化成“保存了当时的值”。
哪些类型语法会消失
interface、类型别名、泛型标注和 as 等静态表达通常不进入 JavaScript 产物。as 声明检查者应如何看待一个表达式,没有转换真实数据。satisfies 检查约束并保留更具体推断,也没有增加运行验证。
interface Input { count: number }
const input = JSON.parse('{"count":"three"}') as Input;
// count 仍是字符串;类型断言没有修改 JSON 值。enum、参数属性和部分命名空间构造具有运行语义,完整编译可能生成代码。Node 的直接 TS 执行能力随版本、语法和开关变化;类型剥离不负责类型检查,不能把“可直接运行”理解为 tsc 已通过。Node TypeScript 说明
收窄让处理顺序可检查
外部数据先以 unknown 进入。检查其实际性质后,可以缩小允许操作范围。any 则放弃相关静态约束,错误更容易沿调用传播。
type SaveResult =
| { kind: 'saved'; id: string }
| { kind: 'rejected'; reason: string };
function message(result: SaveResult): string {
switch (result.kind) {
case 'saved': return `已保存 ${result.id}`;
case 'rejected': return result.reason;
default: {
const impossible: never = result;
return impossible;
}
}
}标签与相关字段一起定义,减少“成功但没有编号”的组合。never 让增加一个分支后遗漏处理成为检查错误。网络里未知的新标签还需要解析和兼容政策。TypeScript 收窄说明
结构相同怎样区分业务身份
TypeScript 主要按成员结构判断兼容。两个仅含 id: string 的记录可能相容,订单编号与用户编号却具有不同业务含义。可以把其使用范围放进不同接口,用品牌类型增加静态区分,并在生成或解析边界建立真实条件。
泛型表达输入输出的联系,例如数组元素类型与取得的元素类型保持一致。没有参与任何成员或操作的泛型参数通常无法提供期望的区分。复杂条件类型值得服务于具体错误防止目标,不能仅以表达能力衡量价值。
TypeScript 有意允许一些编译时无法证明安全的操作。strict 和附加选项缩小风险范围,仍需遵循其真实兼容规则。类型兼容
静态类型怎样连接运行校验
校验库把未知值转换为已检查结果,常能从同一 schema 推导类型。应区别输入与输出:默认值、强制转换和变换会使两者不同。客户端用于反馈,受保护服务入口仍需重新校验并取得当前授权。
schema 检查结构和取值;“订单属于当前用户”“金额没有超过当前余额”依赖当前事实,应在适当执行边界判断。Date、BigInt、十进制和类对象跨 JSON 边界也需要明确传输表示。
共享 schema 适合前后端真的拥有相同契约时。数据库记录、可公开响应和表单输入通常具有不同职责,可以由映射连接,避免把整条数据库记录自动暴露给客户端。
最后更新于