写 TypeScript 最崩溃的不是学类型系统,而是满屏的红色波浪线——你知道它在报错,但你看不懂它在说什么。
Type 'string' is not assignable to type 'never',这句话每个单词我都认识,合在一起就不认识了。我花了一个下午把项目里遇到过的 TS 报错整理了一遍,发现来来回回就是这 10 个。这次不只是告诉你怎么改,而是解释为什么 TypeScript 要这样报错——理解了原因,以后再遇到就不慌了。
1. Type ‘X’ is not assignable to type ‘Y’
出现频率:⭐⭐⭐⭐⭐(几乎每天)
// ❌ 报错
const status: 'active' | 'inactive' = 'pending';
// Type '"pending"' is not assignable to type '"active" | "inactive"'
翻译成人话: 你给的值不在类型允许的范围内。TypeScript 的类型系统是"白名单"机制——只有你声明过的类型才能赋值,其他一律拒绝。
为什么 TypeScript 要这样做? 因为联合类型的目的就是限制值的范围。如果你写了 'active' | 'inactive',就是在告诉 TS “这个变量只可能是这两个值”。如果你传了 'pending',TS 会认为你的代码逻辑有问题——要么你漏了一种状态,要么你赋错了值。
解决方案:
// ✅ 补上缺少的类型
const status: 'active' | 'inactive' | 'pending' = 'pending';
最容易踩的坑: 从接口拿到的数据是 string,但你的类型是联合类型。
// ❌ API 返回的是 string,但你的类型是联合类型
const res = await fetch('/api/user');
const data = await res.json(); // data.status 的类型是 any 或 string
const status: 'active' | 'inactive' = data.status; // 报错
// ✅ 方案一:类型断言(你确定后端返回的值是对的)
const status = data.status as 'active' | 'inactive';
// ✅ 方案二:运行时校验(更安全)
if (data.status === 'active' || data.status === 'inactive') {
const status = data.status; // TS 自动收窄
}
方案二更安全,因为如果后端突然返回了一个你没预期的值(比如 'deleted'),方案一会静默通过,方案二会走到 else 分支让你处理。
2. Property ‘X’ does not exist on type ‘Y’
出现频率:⭐⭐⭐⭐⭐
// ❌ 报错
const user = {};
console.log(user.name);
// Property 'name' does not exist on type '{}'
一句话原因: 对象的类型定义里没有这个属性。
解决方案:
// ✅ 方案一:定义接口
interface User {
name: string;
age: number;
}
const user: User = { name: '张三', age: 25 };
// ✅ 方案二:用 Record
const user: Record<string, unknown> = {};
user.name = '张三';
3. Argument of type ‘X’ is not assignable to parameter of type ‘Y’
出现频率:⭐⭐⭐⭐
// ❌ 报错
function greet(name: string) { return `Hello ${name}`; }
greet(123);
// Argument of type 'number' is not assignable to parameter of type 'string'
一句话原因: 函数参数的类型和你传的值类型不匹配。
解决方案: 传对的类型,或者修改函数签名。
// ✅ 改调用
greet(String(123));
// ✅ 或改函数定义
function greet(name: string | number) { return `Hello ${name}`; }
4. Object is possibly ‘undefined’ / ‘null’
出现频率:⭐⭐⭐⭐
// ❌ 报错
const arr = [1, 2, 3];
const first = arr.find(x => x > 10);
console.log(first.toFixed(2));
// Object is possibly 'undefined'
翻译成人话: 这个值可能不存在,你必须先检查再用。
为什么 TypeScript 要这样做? Array.find() 的返回类型是 T | undefined——因为数组里可能没有符合条件的元素。TypeScript 不允许你在一个可能是 undefined 的值上调用方法,因为运行时会报 TypeError: Cannot read properties of undefined。这个报错其实是 TS 在帮你防 Bug。
三种解决方式(从推荐到不推荐):
// ✅ 方案一:可选链(最推荐,安全且简洁)
console.log(first?.toFixed(2)); // undefined 时返回 undefined,不会崩
// ✅ 方案二:if 守卫(需要在值不存在时做特殊处理)
if (first !== undefined) {
console.log(first.toFixed(2)); // TS 自动收窄为 number
} else {
console.log('没找到');
}
// ⚠️ 方案三:非空断言(你 100% 确定有值时才用)
console.log(first!.toFixed(2)); // 危险:如果实际是 undefined,运行时崩
建议: 默认用 ?.,需要处理不存在的情况用 if,! 只在你能保证值存在时用(比如你刚赋过值)。
5. Type ‘string’ is not assignable to type ‘never’
出现频率:⭐⭐⭐(最让人困惑的报错)
// ❌ 报错
const arr = [];
arr.push('hello');
// Argument of type 'string' is not assignable to parameter of type 'never'
翻译成人话: 空数组没给类型,TypeScript 推断成了 never[]——一个"永远不应该有元素"的数组。
为什么会推断成 never? 这是 TypeScript 类型推断的逻辑链:
- 你写了
const arr = [],没给类型注解 - TS 尝试从初始值推断类型——但数组是空的,没有元素可以参考
- TS 只能推断为"没有任何元素类型",也就是
never[] never是 TypeScript 的底部类型,表示"不可能存在的类型"- 当你
push('hello')时,TS 发现string不是never,报错
解决方案: 声明时给类型,告诉 TS 这个数组将来会放什么。
// ✅ 明确类型
const arr: string[] = [];
arr.push('hello'); // 正常
// ✅ 混合类型
const arr: (string | number)[] = [];
arr.push('hello');
arr.push(42);
延伸: 这就是为什么 TypeScript 社区有一条不成文的规则——空数组永远要手动标类型。靠推断的话,非空数组没问题([1, 2, 3] 能推断为 number[]),空数组一定踩坑。
6. Cannot find module ‘X’ or its corresponding type declarations
出现频率:⭐⭐⭐
// ❌ 报错
import dayjs from 'dayjs';
// Cannot find module 'dayjs' or its corresponding type declarations
一句话原因: 这个包没有 TypeScript 类型定义。
解决方案:
# 方案一:安装社区维护的类型包
npm install -D @types/dayjs
# 方案二:如果 @types 包不存在,创建声明文件
# src/types/dayjs.d.ts
declare module 'dayjs' {
const dayjs: any;
export default dayjs;
}
快速判断: 先 npm install -D @types/包名 试试,大部分流行包都有。没有的话创建 .d.ts 声明文件。
7. Type ‘X | Y’ is not assignable to type ‘X’
出现频率:⭐⭐⭐
// ❌ 报错
function getUser(id: number): User | null {
return db.find(id);
}
const user: User = getUser(1);
// Type 'User | null' is not assignable to type 'User'
一句话原因: 函数可能返回 null,但你用一个不接受 null 的变量接了它。
解决方案:
// ✅ 方案一:处理 null 情况
const user = getUser(1);
if (!user) throw new Error('User not found');
// 此处 user 类型自动收窄为 User
// ✅ 方案二:非空断言(确定有值时)
const user = getUser(1)!;
8. ‘X’ is declared but its value is never read
出现频率:⭐⭐⭐
// ❌ 报错(黄色波浪线)
import { useState, useEffect, useCallback } from 'react';
// 'useCallback' is declared but its value is never read
一句话原因: 你导入了一个东西但没用它。
解决方案: 删掉没用的导入。VS Code 快捷键 Shift + Alt + O 自动清理。
在 tsconfig 里控制这个行为:
{
"compilerOptions": {
"noUnusedLocals": true, // 未使用的变量报错
"noUnusedParameters": true // 未使用的参数报错
}
}
9. ‘this’ implicitly has type ‘any’
出现频率:⭐⭐
// ❌ 报错
const obj = {
name: '张三',
greet() {
setTimeout(function() {
console.log(this.name);
// 'this' implicitly has type 'any'
}, 1000);
}
};
一句话原因: 普通函数的 this 指向不确定,TypeScript 不知道它是什么。
解决方案:
// ✅ 用箭头函数(继承外层 this)
const obj = {
name: '张三',
greet() {
setTimeout(() => {
console.log(this.name); // 正常
}, 1000);
}
};
10. Type ‘X’ is not a valid JSX element
出现频率:⭐⭐(React 项目常见)
// ❌ 报错
function App() {
const Component = condition ? Header : Footer;
return <Component />;
// Type 'typeof Header | typeof Footer' is not a valid JSX element
}
一句话原因: TypeScript 不确定这个组件的类型是合法的 React 组件。
解决方案:
// ✅ 用类型断言
const Component = (condition ? Header : Footer) as React.FC;
// ✅ 或者用 React.ElementType
const Component: React.ElementType = condition ? Header : Footer;
速查表
| 报错关键词 | 通俗翻译 | 最快的解决方式 |
|---|---|---|
| not assignable to type | 类型不匹配 | 检查左右两边的类型定义 |
| does not exist on type | 没有这个属性 | 补充接口定义 |
| possibly undefined/null | 可能是空值 | 加 ?. 或 if 守卫 |
| type ‘never’ | 空数组没给类型 | 声明时加类型 string[] |
| cannot find module | 缺少类型声明 | 装 @types/包名 |
| declared but never read | 导入了没用 | 删掉,或 Shift+Alt+O |
| implicitly has type ‘any’ | this 指向不明 | 改用箭头函数 |
| not a valid JSX element | 组件类型不对 | 用 React.ElementType |
收藏这篇,下次 TypeScript 报错先查这张表,能解决 80% 的红色波浪线。
你遇到过最离谱的 TypeScript 报错是什么?评论区说说,看大家有没有共同的痛苦。

896

被折叠的 条评论
为什么被折叠?



