TypeScript 严格模式在游戏开发中的价值:3251 行代码的"安全网"
> `strict` 模式不是"可选的",它是"必须的"——尤其是在 3251 行的游戏逻辑里。
## 为什么游戏开发需要严格模式
在 Web 开发里,TypeScript 的 `strict` 模式经常被"关掉"——因为 DOM API 太复杂,类型声明经常报错。但在游戏开发里,`strict` 模式是**必须的**——因为游戏逻辑是"状态机",一个类型错误会导致"玩家卡死"、"敌人消失"、"分数乱跳"。
NEON RUNNER 2077 的 `tsconfig.json` 里:
```json
{
"compilerOptions": {
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "Bundler"
}
}
```
## strict 模式挡住了什么
### 1. 隐式 any
```ts
// 没有 strict:可以写
function updateScore(score) { // score 是 any
return score + 10;
}
// 有 strict:必须写
function updateScore(score: number): number {
return score + 10;
}
```
`noImplicitAny` 确保每个参数都有类型。在 `GameScene` 里,`score`、`wave`、`health`、`energy` 都是 `number`,不会被误用。
### 2. 可能为 null 的值
```ts
// 没有 strict:可以写
const keyboard = this.input.keyboard;
keyboard.on('keydown-UP', ...); // 如果 keyboard 是 null,运行时报错
// 有 strict:必须检查
const keyboard = this.input.keyboard;
if (!keyboard) return;
keyboard.on('keydown-UP', ...);
```
`strictNullChecks` 确保 `this.input.keyboard` 可能为 `null` 时,必须先检查再使用。`MenuScene` 里的 `setupInput()` 就是这么写的。
### 3. 未使用的变量
```ts
// 没有 unused:可以写
onUpdate: (_tween, _key, _target, current) => { ... }
// 有 unused:必须用下划线前缀
onUpdate: (_tween: Phaser.Tweens.Tween, _key: string, _target: unknown, current: number) => { ... }
```
`noUnusedLocals` 和 `noUnusedParameters` 确保未使用的变量必须用 `_` 前缀。`PreloadScene` 里的 `onUpdate` 回调就是这么写的。
### 4. switch 语句的穿透
```ts
// 没有 fallthrough:可以写
case 1:
console.log('one');
case 2:
console.log('two'); // 会执行两次
// 有 fallthrough:必须写 break
case 1:
console.log('one');
break;
case 2:
console.log('two');
```
`noFallthroughCasesInSwitch` 确保 `case` 语句不会"穿透"。`MenuScene` 里的 `confirmSelection()` 就是这么写的。
## Phaser 类型声明:最大的坑
Phaser 3 的类型声明是最大的坑。`phaser` 包的类型声明在 `@types/phaser` 里,但 Phaser 3.90 的类型声明和 3.80 不完全兼容。NEON RUNNER 2077 里用的是 `phaser@^3.80.1`,但实际安装的是 3.90——类型声明可能有差异。
解决方法是:
```ts
// tsconfig.json
{
"compilerOptions": {
"maxNodeModuleJsDepth": 5, // 深入 node_modules 找类型
"skipLibCheck": true, // 跳过 .d.ts 的类型检查
}
}
```
`skipLibCheck: true` 跳过第三方库的类型检查——这是必要的,因为 Phaser 的类型声明有时会报错。
## 类型守卫:运行时类型检查
在 `MenuScene` 的鼠标点击检测里,`pointer` 的类型是 `Phaser.Input.Pointer`。这个类型有 `x`、`y`、`position` 等属性,但 Phaser 的类型声明里没有 `position`——所以代码里用的是 `x` 和 `y`。
```ts
this.input.on('pointerdown', (pointer: Phaser.Input.Pointer) => {
// pointer.x 和 pointer.y 是安全的
// pointer.position 是未定义的(类型声明里没有)
});
```
## 类型推断:让编译器帮你写代码
TypeScript 的类型推断在游戏开发里非常有用:
```ts
// 推断为 number
const score = 0;
// 推断为 string
const label = '> JACK IN // START GAME';
// 推断为 Phaser.GameObjects.Text
const text = this.add.text(x, y, 'NEON RUNNER', {
fontFamily: FONTS.DISPLAY,
fontSize: '56px',
color: '#FCEE0A',
});
```
`text` 的类型被推断为 `Phaser.GameObjects.Text`,所以 IDE 会自动补全 `setAlpha()`、`setOrigin()`、`setDepth()` 等方法。
## 下一章预告
TypeScript 严格模式讲完了,但 NEON RUNNER 2077 的构建系统里还有一个关键话题——**Vite 构建配置与 Phaser 分块优化**。`vite.config.ts` 里那些配置项是怎么影响产物大小的?下一章我会拆解这个设计。
> 严格模式的核心原则:**类型即文档**。如果一个变量的类型不确定,它就不是"可维护的"。
- 点赞
- 收藏
- 关注作者
评论(0)