TypeScript 严格模式在游戏开发中的价值:3251 行代码的"安全网"

举报
yd_296221794 发表于 2026/10/02 08:44:05 2026/10/02
【摘要】 > `strict` 模式不是"可选的",它是"必须的"——尤其是在 3251 行的游戏逻辑里。## 为什么游戏开发需要严格模式在 Web 开发里,TypeScript 的 `strict` 模式经常被"关掉"——因为 DOM API 太复杂,类型声明经常报错。但在游戏开发里,`strict` 模式是**必须的**——因为游戏逻辑是"状态机",一个类型错误会导致"玩家卡死"、"敌人消失"、"...

> `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` 里那些配置项是怎么影响产物大小的?下一章我会拆解这个设计。


> 严格模式的核心原则:**类型即文档**。如果一个变量的类型不确定,它就不是"可维护的"。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。