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(); // 正确!
}
相关推荐
一条大祥脚2 小时前
势能分析与势能线段树
开发语言·javascript·数据结构·算法
jarreyer2 小时前
【AI编程】claudecode插件配置记录和trae软件相关配置
前端·chrome·ai编程
ヤ鬧鬧o.2 小时前
HTML多倒计时管理
前端·javascript·css·html5
知兀2 小时前
【uniapp/vue3+ts/js】eslint9+prettier+husky+lint-staged
前端·javascript·uni-app
小北方城市网2 小时前
Spring Cloud Gateway 动态路由进阶:基于 Nacos 配置中心的热更新与版本管理
java·前端·javascript·网络·spring boot·后端·spring
码上出彩2 小时前
H5+CSS3响应式设计实战:基于Flex布局的适配方案
前端·css·css3
wqwqweee2 小时前
Flutter for OpenHarmony 看书管理记录App实战:关于我们实现
android·javascript·python·flutter·harmonyos
你说爱像云 要自在漂浮才美丽2 小时前
【HTML5与CSS3】
前端·css3·html5
倪枫2 小时前
CSS3——文本样式(字体样式和文本布局)
前端·css·css3