TypeScript 和 JavaScript 的 'use strict' 有啥不同

都叫严格模式,但它们解决的问题完全不在一个层次上

前言

写完 JavaScript 严格模式的文章,突然想到一个问题:"TypeScript 不也有个 strict: true 吗?这俩是一回事吗?开了 TS 的 strict 还要写 'use strict' 吗?"

说实话,我刚学 TypeScript 时也搞混过。看着 tsconfig.json 里的 strict: true,心想这应该和 JS 的 'use strict' 差不多吧,结果配完发现代码里还是满屏标红。

后来花了个周末把 TypeScript 编译选项挨个试了一遍,才明白:这俩虽然名字像,但压根不是一个维度的东西------一个管编译时的类型检查,一个管运行时的语言行为。

先抛几个问题,看看你是不是也有同样的困惑:

  • TypeScript 的 strict 和 JavaScript 的 'use strict' 到底啥区别?
  • 开了 TS 的 strict 模式,还需要写 'use strict' 吗?
  • 它们检查的东西一样吗?(答案是完全不一样)
  • 为啥名字这么像,却是两个东西?(这锅 TypeScript 团队真得背)

这篇文章就来聊聊,同样是"严格",它们到底严在哪里,又有什么本质区别。


目录


一个真实的困惑:我到底该开哪个?

先看一个常见场景。你在写 TypeScript 项目,tsconfig.json 里配了:

json 复制代码
{
  "compilerOptions": {
    "strict": true
  }
}

然后在代码里写:

lua 复制代码
function greet(name) {  // TS 报错:Parameter 'name' implicitly has an 'any' type
  console.log('Hello ' + name);
}

TypeScript 立马给你标红了。你想:行,TypeScript 的严格模式生效了

但是,这时候你在文件顶部加不加 'use strict',会有区别吗?

或者反过来,如果你只写了 'use strict',没开 TypeScript 的 strict: true,又会怎样?

这就是今天要搞清楚的问题


JavaScript 严格模式回顾:运行时的守护者

先快速回顾一下 JavaScript 的严格模式(详细内容可以看上一篇文章)

它是什么?

一个运行时开关,在代码执行时改变 JavaScript 引擎的行为。

javascript 复制代码
'use strict';  // 告诉 JS 引擎:"用严格模式跑这段代码"
​
x = 10;  // ReferenceError: x is not defined(运行时报错)

它解决什么?

JavaScript 早期设计的语言层面的问题

  • 运行时错误:把静默失败变成抛出异常
  • 危险语法 :禁止容易出错的语法(比如 with、八进制字面量)
  • 意外行为:修正反直觉的行为(比如自动创建全局变量)

关键特征

mindmap root((JavaScript
严格模式)) 运行时生效 代码执行时检查 依赖 JS 引擎 无法在编译时发现问题 语言层面 修改语言行为 禁止危险语法 修正历史问题 向后兼容 老代码不受影响 需要主动开启 只影响声明的作用域

TypeScript 严格模式:编译时的守护者

TypeScript 的 strict: true 是另一个完全不同的东西。

它是什么?

一个编译选项集合 ,在代码编译(转换为 JS)之前进行类型检查

json 复制代码
// tsconfig.json
{
  "compilerOptions": {
    "strict": true  // 这是个"总开关"
  }
}

当你开启 strict: true,实际上是同时开启了这 7 个编译选项:

json 复制代码
{
  "compilerOptions": {
    "strict": true,  // 👆 等价于下面 👇
​
    "noImplicitAny": true,               // 禁止隐式 any 类型
    "noImplicitThis": true,              // 禁止 this 有隐式 any 类型
    "strictNullChecks": true,            // 严格的 null/undefined 检查
    "strictFunctionTypes": true,         // 严格的函数类型检查
    "strictBindCallApply": true,         // 严格检查 bind/call/apply
    "strictPropertyInitialization": true,// 严格的类属性初始化检查
    "alwaysStrict": true,                // 始终以严格模式解析(会加 'use strict')
    "useUnknownInCatchVariables": true   // catch 变量默认为 unknown 类型
  }
}

等等,看到 alwaysStrict 了吗?这就是联系的地方!

它解决什么?

TypeScript 的严格模式解决的是类型安全问题

  • 编译时错误:在代码运行前就发现类型错误
  • 类型推断 :强制明确类型,避免隐式 any
  • 空值安全 :防止 null/undefined 引起的运行时错误
  • 函数安全:确保函数调用的类型正确性

关键特征

mindmap root((TypeScript
严格模式)) 编译时生效 转译前检查 IDE 实时提示 运行前发现问题 类型系统层面 强制类型明确 空值安全检查 函数类型检查 配置灵活 总开关 可单独开关每个选项 逐步迁移友好

核心差异:编译时 vs 运行时

现在重点来了,这两者的本质区别

1. 生效时机不同

flowchart LR A[编写代码] --> B[TypeScript 编译] B --> C[生成 JavaScript] C --> D[浏览器/Node.js 执行] B -.->|TypeScript strict| E[编译时检查] D -.->|JavaScript 'use strict'| F[运行时检查] style E fill:#e1f5ff style F fill:#fff4e1

TypeScript strict: true

  • ✅ 在编译阶段检查(你还在写代码的时候)
  • ✅ IDE 实时提示,根本不让你编译通过
  • ✅ 问题在开发阶段就被发现

JavaScript 'use strict'

  • ✅ 在运行阶段检查(代码已经在跑了)
  • ✅ 只有执行到那行代码才会报错
  • ✅ 问题可能在生产环境才暴露

2. 检查内容不同

检查项 TypeScript strict JavaScript 'use strict'
未声明变量 ❌ 不检查(这是 JS 运行时的事) ✅ 运行时报错
隐式 any 类型 ✅ 编译错误 ❌ 不检查(JS 没有类型)
null/undefined 安全 ✅ 编译错误 ❌ 不检查(运行时才知道是否为 null)
函数参数类型 ✅ 编译错误 ❌ 不检查
只读属性赋值 ✅ 编译错误(如果用了 readonly ✅ 运行时报错
重复参数名 ✅ 编译错误 ✅ 运行时报错
八进制字面量 ✅ 编译错误 ✅ 运行时报错
with 语句 ✅ 编译错误 ✅ 运行时报错
this 为 undefined ✅ 类型检查会提示 ✅ 运行时行为改变

3. 适用范围不同

TypeScript strict

  • 只在 .ts.tsx 文件中生效
  • 需要 TypeScript 编译器
  • 编译后的 JS 文件里没有类型信息

JavaScript 'use strict'

  • 在所有 JS 文件中都能用(.js.ts 编译后的文件)
  • 不需要任何工具,浏览器原生支持
  • 直接影响 JS 引擎的行为

深入对比:它们分别解决什么问题?

案例 1:未声明的变量

JavaScript 'use strict' 能捕获

javascript 复制代码
'use strict';
​
function test() {
  myVar = 10;  // ❌ ReferenceError: myVar is not defined(运行时)
}
​
test();

TypeScript strict 不检查这个

csharp 复制代码
// tsconfig.json: { "strict": true }
​
function test() {
  myVar = 10;  // ⚠️ TypeScript: Cannot find name 'myVar'
               // 但这是因为 TypeScript 要求先声明变量
               // 不是因为 strict 模式
}

TypeScript 编译后:

javascript 复制代码
"use strict";  // 👈 注意这里!因为 alwaysStrict: true
​
function test() {
  myVar = 10;  // 运行时还是会被 'use strict' 捕获
}

结论

  • TS 的 strict 本身不处理未声明变量
  • strict 包含 alwaysStrict,会自动加 'use strict'
  • 最终还是靠 JS 的严格模式在运行时捕获

案例 2:隐式 any 类型

TypeScript strict 能捕获

lua 复制代码
// strict: true
​
function greet(name) {
  // ❌ 编译错误:Parameter 'name' implicitly has an 'any' type
  console.log('Hello ' + name);
}

JavaScript 'use strict' 完全不管

javascript 复制代码
'use strict';
​
function greet(name) {
  // ✅ 没问题,JS 本来就是动态类型
  console.log('Hello ' + name);
}

结论

  • TS 的 strict 强制你明确类型
  • JS 的 'use strict' 对类型无能为力(因为 JS 没有静态类型)

案例 3:空值安全

TypeScript strict 的强项

php 复制代码
// strict: true(包含 strictNullChecks)
​
function getLength(str: string) {
  return str.length;
}
​
const maybeStr: string | null = getSomeString();
​
getLength(maybeStr);
// 编译错误:Argument of type 'string | null' is not assignable to parameter of type 'string'

JavaScript 'use strict' 无能为力

javascript 复制代码
'use strict';
​
function getLength(str) {
  return str.length;
}
​
const maybeStr = getSomeString();
​
getLength(maybeStr);
// 编译通过
// 运行时如果 maybeStr 是 null,会报错:Cannot read property 'length' of null

结论

  • TS 的 strict 在编译时就发现了潜在的 null 引用问题
  • JS 的 'use strict' 只能等到运行时才崩溃

案例 4:函数 this 类型

两者都有帮助,但方式不同

typescript 复制代码
// TypeScript strict
interface User {
  name: string;
  greet(this: User): void;  // 明确 this 类型
}
​
const user: User = {
  name: 'Alice',
  greet() {
    console.log(this.name);
  }
};
​
const greetFn = user.greet;
greetFn();
// ❌ TS 编译错误:The 'this' context of type 'void' is not assignable to method's 'this' of type 'User'
javascript 复制代码
// JavaScript 'use strict'
'use strict';
​
const user = {
  name: 'Alice',
  greet() {
    console.log(this.name);  // this 是 undefined
  }
};
​
const greetFn = user.greet;
greetFn();
// ✅ 编译通过
// 运行时报错:Cannot read property 'name' of undefined

结论

  • TS 的 strict 通过类型系统在编译时就警告你
  • JS 的 'use strict'thisundefined,在运行时才报错

实战案例:看看它们如何配合工作

完整示例:两者互补

json 复制代码
// tsconfig.json
{
  "compilerOptions": {
    "strict": true  // 包含 alwaysStrict: true
  }
}
typescript 复制代码
// user.ts
​
// 1️⃣ TypeScript 的 strict 检查类型
function calculateTotal(price: number, quantity: number): number {
  // 2️⃣ TypeScript 确保参数类型正确
  if (price < 0) {
    // 3️⃣ strictNullChecks 确保不返回 undefined
    throw new Error('Price cannot be negative');
  }
​
  return price * quantity;
}
​
// 4️⃣ 编译时就发现类型错误
// calculateTotal('100', 5);  // ❌ 编译错误
​
// 5️⃣ 如果不小心写了未声明变量
function buggyCode() {
  totol = 100;  // ❌ TS: Cannot find name 'totol'
}

编译后的 JavaScript:

javascript 复制代码
// user.js
"use strict";  // 👈 自动加上!来自 alwaysStrict: true
​
// TypeScript 的类型检查已经完成,这里只剩运行时代码
function calculateTotal(price, quantity) {
  if (price < 0) {
    throw new Error('Price cannot be negative');
  }
  return price * quantity;
}
​
// 如果 TypeScript 没拦住(比如用了 any),运行时会拦住
function buggyCode() {
  totol = 100;  // 💥 ReferenceError(被 'use strict' 捕获)
}

双重保险

  1. 第一层(编译时) :TypeScript 的 strict 检查类型、空值、函数签名
  2. 第二层(运行时) :JavaScript 的 'use strict' 检查语言层面的问题

深入理解:为什么需要两者?

JavaScript 严格模式的局限

'use strict' 再严格,也只是让错误暴露得早一点,但:

  • ❌ 不能阻止类型错误(比如把字符串传给期望数字的函数)
  • ❌ 不能保证空值安全(比如访问 null 的属性)
  • ❌ 不能检查函数签名(比如参数数量、类型)

TypeScript 严格模式的局限

strict: true 再强大,也只在编译时有效,但:

  • ❌ 不能处理动态引入的第三方库(没有类型定义的)
  • ❌ 不能检查运行时的值(比如从 API 返回的数据)
  • ❌ 如果用了 any 或类型断言,类型检查就被绕过了

两者互补

graph LR A[开发阶段] --> B[TypeScript strict
类型检查] B --> C[编译] C --> D[运行阶段] D --> E[JavaScript 'use strict'
语言规则检查] B -.-> F[捕获类型错误
空值引用
函数签名问题] E -.-> G[捕获未声明变量
静默失败
危险语法] style B fill:#e1f5ff style E fill:#fff4e1

最佳组合

  • TypeScript strict: true:在开发时就把大部分问题拦住
  • JavaScript 'use strict' (自动加上):作为最后一道防线,拦住 TypeScript 也管不了的运行时问题

✅ 最佳实践:该怎么配置?

1. 新 TypeScript 项目:两个都要

json 复制代码
// tsconfig.json
{
  "compilerOptions": {
    "strict": true,  // 👈 已经包含 alwaysStrict: true
    "target": "ES2020",
    "module": "ESNext"
  }
}

这样配置后:

  • ✅ TypeScript 会做编译时检查
  • ✅ 自动为每个文件加上 'use strict'
  • 你不需要手写 'use strict'

2. 老 TypeScript 项目:逐步迁移

如果直接开 strict: true 会导致满屏报错,可以单独开启

json 复制代码
{
  "compilerOptions": {
    "strict": false,  // 先不开总开关
​
    // 逐步开启单个选项
    "noImplicitAny": true,            // 第一步:禁止隐式 any
    "alwaysStrict": true,             // 第二步:加 'use strict'
    "strictNullChecks": true,         // 第三步:空值检查
    // ... 逐步开启其他选项
  }
}

3. 纯 JavaScript 项目:只能用 'use strict'

如果你不用 TypeScript,那就只能用 JavaScript 的严格模式:

javascript 复制代码
// 方式 1:全局开启(文件顶部)
'use strict';
​
// 你的代码...
csharp 复制代码
// 方式 2:函数级别开启
function myFunction() {
  'use strict';
  // 只在这个函数内严格
}

推荐:配合 ESLint 强制添加:

java 复制代码
// .eslintrc.js
module.exports = {
  rules: {
    'strict': ['error', 'global']
  }
};

4. 配合 ESLint/Prettier

TypeScript 的 strict 模式专注类型检查,但代码质量还需要 ESLint:

perl 复制代码
// .eslintrc.json
{
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:@typescript-eslint/recommended-requiring-type-checking"
  ],
  "parserOptions": {
    "project": "./tsconfig.json"
  }
}

这样你会得到:

  • TypeScript strict:类型安全
  • ESLint:代码质量、最佳实践
  • Prettier:代码格式

对比总结表

维度 TypeScript strict: true JavaScript 'use strict'
本质 编译选项集合 运行时指令
生效时机 编译时(写代码时) 运行时(代码执行时)
检查内容 类型、空值、函数签名 语言规则、危险语法
错误提示 IDE 实时提示、编译失败 运行时抛出异常
依赖 TypeScript 编译器 JavaScript 引擎
适用文件 .ts.tsx 所有 .js 文件
性能影响 无(编译时) 微小(运行时)
向后兼容 需要 TypeScript 所有现代浏览器
配置方式 tsconfig.json 代码中写 'use strict'
关联关系 alwaysStrict 会自动加 'use strict' 无关 TypeScript
最佳实践 新项目必开 TS 项目自动加上,JS 项目手动加

常见误区澄清

误区 1:"开了 TypeScript strict 就不需要 'use strict' 了"

错误

虽然 strict: true 包含 alwaysStrict: true(会自动加 'use strict'),但:

  • TypeScript 只检查编译时的类型问题
  • 'use strict' 检查运行时的语言问题

正确理解 :开了 strict: true 后,编译出的 JS 会自动带 'use strict',所以你不用手写。


误区 2:"'use strict' 能替代 TypeScript"

错误

'use strict' 再严格,也不能做类型检查。比如:

javascript 复制代码
'use strict';
​
function add(a, b) {
  return a + b;
}
​
add('1', 2);  // ✅ 运行通过,结果是 '12'(字符串拼接)

TypeScript 会在编译时就发现类型问题:

lua 复制代码
function add(a: number, b: number) {
  return a + b;
}
​
add('1', 2);  // ❌ 编译错误:Argument of type 'string' is not assignable to parameter of type 'number'

误区 3:"strict: true 太严格了,影响开发效率"

错误(短期看似如此,长期受益)

刚开始确实会遇到很多类型错误,但:

  • 这些错误本来就存在,只是以前被隐藏了
  • 在编译时发现远比在生产环境崩溃要好
  • 类型提示会让重构和协作更安全

建议 :新项目直接开 strict: true,老项目逐步迁移。


写在最后

研究完这两个"严格模式",我的理解是:

它们的关系

  • TypeScript strict:编译时的守护者,拦截类型错误、空值引用、函数签名问题
  • JavaScript 'use strict' :运行时的守护者,拦截语言层面的危险语法和意外行为
  • 它们不是替代关系,而是互补关系

为什么要两者都用

  • TypeScript 再强大,也只在编译时有效
  • 编译后的 JS 代码,依然需要 'use strict' 在运行时提供保护
  • strict: true 里的 alwaysStrict 会自动加上 'use strict',所以你只需要配置 TypeScript,不用手写

使用建议

  1. TypeScript 项目 :开启 strict: true(已包含 alwaysStrict
  2. 纯 JavaScript 项目 :手动加 'use strict',配合 ESLint 强制
  3. 不要因为名字相似就混淆它们:一个管编译时类型,一个管运行时语言规则

下次有人问你"TypeScript 的 strict 和 JavaScript 的 'use strict' 有啥区别",你可以自信地说:

一个在编译时保护你的类型安全,一个在运行时保护你的代码行为。名字像,但完全不是一回事!

TypeScript 官方文档

  1. Compiler Options: strict - TypeScript 严格模式官方说明
  2. TSConfig Reference - 完整的编译选项参考

JavaScript 官方规范

  1. ECMAScript Strict Mode - 严格模式的官方定义
  2. MDN - Strict mode - 最全面的严格模式文档

相关文档

  1. TypeScript Deep Dive: Strict - 深入理解 TypeScript 严格性
  2. JavaScript: The Good Parts - Douglas Crockford 讲解严格模式的设计哲学
相关推荐
恒创科技HK3 小时前
香港服务器速度快慢受何影响?
运维·服务器·前端
bubiyoushang8884 小时前
MATLAB实现直流电法和大地电磁法的一维正演计算
前端·javascript·matlab
Mintopia4 小时前
🧠 AIGC模型的增量训练技术:Web应用如何低成本迭代能力?
前端·javascript·aigc
Mintopia4 小时前
🧩 Next.js在国内环境的登录机制设计:科学、务实、又带点“国风味”的安全艺术
前端·javascript·全栈
雨过天晴而后无语4 小时前
Windchill中MVC选中事件级联另一MVC内容
java·javascript·html·mvc
qq. 28040339844 小时前
react hooks
前端·javascript·react.js
LHX sir5 小时前
什么是UIOTOS?
前端·前端框架·编辑器·团队开发·个人开发·web
Gazer_S5 小时前
【前端状态管理技术解析:Redux 与 Vue 生态对比】
前端·javascript·vue.js
小光学长5 小时前
基于Vue的图书馆座位预约系统6emrqhc8(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
前端·数据库·vue.js