ts问题记录

15 阅读5分钟

基础类型

关于数组

1. 我想声明一个number组成的数组,可以声明的方式如下

简单类型

优先使用 number[] ,因为它更简洁,社区更常见

//一维数组
let list: number[] = [1, 2, 3];
//二维数组
let list: number[][] = [1, 2, 3];
// 元组
let list: [number, number, number] = [1, 2, 3];

复杂类型

在需要明确泛型或复杂类型时,可以使用 Array<T>

//泛型
let list: Array<number> = [1, 2, 3];
let matrix: Array<Array<number>> = [[1, 2], [3, 4]]; // 二维数组
let mixed: Array<number | string> = [1, "two", 3];   // 联合类型数组

关于枚举

1. 先说一个场景:enum Color {Red = 1, Green, Blue} let c: Color = Color.Green;打印c会得到什么?

deepseek解答: 这是一个很好的问题!虽然 Color.Green 的值确实是 2(一个数字),但 TypeScript 推荐将变量 c 声明为 Color 类型(枚举类型)而不是 number,原因如下:


1.1 语义化更强,提高代码可读性
  • Color 类型 明确表示 c 只能是 Color.RedColor.GreenColor.Blue,而不是任意数字。
  • 如果声明为 numberc 可以被赋值为 45 或任何数字,但逻辑上它应该只代表 Color 的枚举值。

示例对比:

// ✅ 正确:c 只能是 Color 的成员
let c: Color = Color.Green; // 只能 = Color.Red / Color.Green / Color.Blue

// ❌ 不推荐:c 可以是任意数字,失去类型约束
let c: number = Color.Green; 
c = 100; // 逻辑错误,但 TypeScript 不会报错

1.2 类型安全,避免无效值
  • 如果 cColor 类型,尝试赋值非法值会触发 TypeScript 错误:
    let c: Color = Color.Green;
    c = 99; // ❌ TS 报错:不能将 99 分配给类型 Color
    
  • 如果 cnumber,则无法检测这种逻辑错误:
    let c: number = Color.Green;
    c = 99; // ✅ TS 不报错,但逻辑错误(99 不是有效的 Color)
    

1.3 便于代码维护和重构
  • 如果未来 Color 的取值变化(例如 Green2 改为 3),只需修改 enum 定义,所有用到 Color 类型的地方会自动适应。
  • 如果直接使用 number,你需要手动检查所有用到 2 的地方是否应该改为 3,容易出错。

1.4 编辑器智能提示(Autocomplete)
  • 声明为 Color 类型时,编辑器(如 VS Code)会提供枚举成员的自动补全:
    let c: Color = Color. // 输入时会提示 Red、Green、Blue
    
  • 如果声明为 number,则无法获得这种提示。

1.5 运行时仍可反向映射(数字枚举的特性)

虽然 c 的值是 2,但数字枚举允许反向查找名称:

console.log(Color[c]); // 输出 "Green"(如果 c = 2)

如果 cnumber 类型,这种反向映射会失去关联性。


什么时候应该用 number 代替 Color

极少数情况下,如果你确实需要 c 接受任意数字(而不仅仅是枚举值),才声明为 number。但这种情况通常意味着设计有问题,应该优先考虑使用枚举。


总结
  • let c: Color:表示 cColor 枚举的成员,类型更严格,代码更安全、可读。
  • let c: number:失去语义约束,允许任意数字,不推荐。

TypeScript 的枚举本质上是编译时类型 + 运行时对象,既提供类型检查,又保留实际的 JavaScript 值(这里是数字 2),所以最佳实践是声明为枚举类型。

关于断言

1. 一个any类型的变量,使用时如果不使用类型断言(<string>someValue),直接写 someValue.length 也能正常运行,为什么还要加断言?(deepseek解答)

问题分析

你提供的代码片段:

let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;

问题:如果不使用类型断言(<string>someValue),直接写 someValue.length 也能正常运行,为什么还要加断言?


1.1 不加断言的情况

如果直接写:

let strLength: number = someValue.length; // 不报错,能运行
  • 可以运行:因为 someValueany 类型,TypeScript 不会对其做任何类型检查。
  • 风险:如果 someValue 不是字符串(比如是 number),访问 .length 会导致运行时错误(如 undefined 或报错)。

1.2 加断言的好处
(1) 明确类型意图
let strLength: number = (<string>someValue).length;
  • 显式告诉编译器:“我确定 someValuestring,请按字符串类型处理”。
  • 即使 someValueany,断言会强制类型检查,避免后续误操作。
(2) 提供编译时类型安全

如果 someValue 可能不是字符串,断言会帮助提前发现问题:

let someValue: any = 123; // 不是字符串
let strLength: number = (<string>someValue).length; // ❌ 编译时不会报错,但运行时报错!

虽然运行时仍可能出错,但断言是一种开发者主动承诺的标记,提醒你确保逻辑正确。

(3) 配合 --strictNullChecks 更安全

如果开启严格模式,断言可以排除 null/undefined

let someValue: string | null = getStringOrNull(); // 可能返回 null
let strLength: number = (<string>someValue).length; // ❌ 不安全,可能运行时报错
let safeLength: number = someValue!.length; // ✅ 非空断言(需确保不为 null)

1.3 更推荐的方式:类型守卫

断言是“强制告诉编译器”,而类型守卫是“让编译器智能推断”:

if (typeof someValue === "string") {
  let strLength: number = someValue.length; // 安全,编译器知道是 string
}
  • 优势:运行时安全,无需手动断言。

1.4 何时使用断言?
  • any 缩小类型:当你比编译器更清楚变量的实际类型时(如动态解析的 API 响应)。
  • 临时绕过复杂类型问题(但应尽量用更安全的方式替代)。

1.5 断言 vs. 不加断言对比
场景不加断言加断言
类型安全性❌ 无检查(any 的隐患)✅ 显式声明类型
运行时风险可能隐藏错误需开发者自己保证正确性
推荐程度❌ 尽量避免✅ 必要时使用,但优先用类型守卫

总结
  • 不加断言:能运行,但失去类型安全性(尤其是 any 类型)。
  • 加断言:明确类型意图,但需自行确保逻辑正确。
  • 最佳实践
    1. 尽量避免 any,用更精确的类型。
    2. 优先用类型守卫typeofinstanceof)代替断言。
    3. 断言仅作为“最后手段”。