TypeScript 类型断言

TypeScript 类型断言

一、类型断言

举个简单例子:

typescript 复制代码
// 定义一个只能是 'a'/'b'/'c' 的类型
type T = 'a'|'b'|'c';
// TS 推断 foo 的类型是 string(太宽泛了)
let foo = 'a';
// 报错:string 类型不能赋值给 T 类型
let bar:T = foo;

这里 foo 明明是 'a',但 TS 推断成了 string 类型(T 的父类型),父类型不能赋值给子类型,就报错了。

类型断言就是用来"绕开"TS 的自动推断,手动指定值的类型------你比编译器更了解自己的代码,直接告诉它该怎么识别这个值!

改一下上面的例子,加上断言就不报错了:

typescript 复制代码
type T = 'a'|'b'|'c';
let foo = 'a';
// 断言 foo 的类型是 T,TS 直接照做
let bar:T = foo as T; // 正确!

注意:类型断言不会真的改变值的类型,只是给编译器"提个醒",运行时该是什么类型还是什么类型。

二、类型断言的两种写法(推荐第二种)

TS 提供了两种断言语法,功能完全一样,只是写法不同:

typescript 复制代码
// 写法1:尖括号形式(和 JSX 语法冲突,不推荐)
let bar:T = <T>foo;

// 写法2:as 形式(推荐!无冲突,更易读)
let bar:T = foo as T;

因为 React 的 JSX 语法也用尖括号(比如 <div>),为了避免混淆,现在都用 值 as 类型 的写法。

例子:操作 DOM 元素

日常开发中最常用的场景------获取输入框并读取 value 属性:

typescript 复制代码
// TS 推断 username 的类型是 HTMLElement | null
const username = document.getElementById('username');

if (username) {
  // 断言成输入框类型,才能读取 value 属性
  (username as HTMLInputElement).value; // 正确!
}

这里的圆括号不能少,否则 TS 会把 username as HTMLInputElement.value 当成整体,直接报错

三、类型断言不可以随便使用

你不能把一个值断言成完全无关的类型,TS 会拦着你:

typescript 复制代码
const n = 1;
// 报错:数值不能断言成字符串(完全无关)
const m:string = n as string;

断言的规则:实际类型和断言类型必须"兼容"------要么是父子类型,要么是子父类型(比如 string 可以断言成 'a'|'b',反过来也可以)。

如果非要断言成完全无关的类型(不推荐),可以走"弯路":先断言成 unknown(所有类型的父类型),再断言成目标类型:

typescript 复制代码
const n = 1;
// 先转 unknown,再转 string,不报错了
const m:string = n as unknown as string;

四、超实用的特殊断言:as const

日常开发中,我们经常遇到"let 变量被推断成宽泛类型"的问题,比如:

typescript 复制代码
let s = 'JavaScript';
// 定义只能传这三个值的类型
type Lang = 'JavaScript'|'TypeScript'|'Python';

function setLang(language:Lang) {}

// 报错:string 类型不能传给 Lang 类型
setLang(s);

解决方法有两个:要么把 let 改成 const(TS 会推断成固定值类型),要么用 as const 断言:

typescript 复制代码
// 断言后,s 的类型变成 'JavaScript'(不是 string 了)
let s = 'JavaScript' as const;
setLang(s); // 正确!

as const 的核心作用:把值断言成"不可变的常量类型",缩小 TS 的推断范围。

as const 还能用于对象/数组

typescript 复制代码
// 普通声明:x/y 都是 number 类型
const v1 = { x: 1, y: 2 };

// 断言整个对象:x=1、y=2,且只读
const v2 = { x: 1, y: 2 } as const;

// 普通数组:number[] 类型
const a1 = [1,2,3];
// 断言后:只读元组 [1,2,3]
const a2 = [1,2,3] as const;

比如数组断言成元组后,传给固定参数的函数就不报错了:

typescript 复制代码
function add(x:number, y:number) {
  return x + y;
}

const nums = [1,2] as const;
add(...nums); // 正确!

五、非空断言:!(保证值不是 null/undefined)

如果一个值可能是 null/undefined,但你确定它一定有值,用 ! 做非空断言:

typescript 复制代码
// TS 担心 root 是 null,调用方法会报错
const root = document.getElementById('root');
// root.addEventListener('click', () => {}); // 报错

// 加 ! 断言:root 一定不是 null
const root = document.getElementById('root')!;
root.addEventListener('click', () => {}); // 正确!

非空断言别滥用!只有你确定值非空时才用,否则运行时可能报错。更稳妥的方式是手动判断:

typescript 复制代码
const root = document.getElementById('root');
if (root) {
  root.addEventListener('click', () => {});
}

六、断言函数:确认参数类型

有时候我们需要写"校验函数",确保参数符合类型要求,不符合就抛错------这就是断言函数:

typescript 复制代码
// 新版写法:明确告诉 TS,这个函数断言 value 是字符串
function isString(value:unknown):asserts value is string {
  if (typeof value !== 'string') {
    throw new Error('不是字符串!');
  }
}

// 使用断言函数
function toUpper(x: string|number) {
  isString(x); // 执行后,TS 就知道 x 是字符串了
  return x.toUpperCase(); // 正确!
}
相关推荐
掘金安东尼7 小时前
纯 CSS 实现弹性文字效果
前端·css
牛奶8 小时前
Vue 基础理论 & API 使用
前端·vue.js·面试
牛奶8 小时前
Vue 底层原理 & 新特性
前端·vue.js·面试
anOnion8 小时前
构建无障碍组件之Radio group pattern
前端·html·交互设计
pe7er8 小时前
状态提升:前端开发中的状态管理的设计思想
前端·vue.js·react.js
SoaringHeart9 小时前
Flutter调试组件:打印任意组件尺寸位置信息 NRenderBox
前端·flutter
晚风予星10 小时前
Ant Design Token Lens 迎来了全面升级!支持在 .tsx 或 .ts 文件中直接使用 Design Token
前端·react.js·visual studio code
sunny_10 小时前
⚡️ vite-plugin-oxc:从 Babel 到 Oxc,我为 Vite 写了一个高性能编译插件
前端·webpack·架构
GIS之路10 小时前
ArcPy 开发环境搭建
前端
林小帅12 小时前
【笔记】OpenClaw 架构浅析
前端·agent