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 分钟前
css实现一个宽高固定百分比的布局的一个方式
前端
剪刀石头布啊5 分钟前
js数组之快速组、慢数组、密集数组、稀松数组
前端
mango_mangojuice27 分钟前
Linux学习笔记(make/Makefile)1.23
java·linux·前端·笔记·学习
Days20501 小时前
简单处理接口返回400条数据本地数据分页加载
前端
Novlan11 小时前
@tdesign/uniapp 图标瘦身
前端
ManThink Technology1 小时前
如何使用EBHelper 简化EdgeBus的代码编写?
java·前端·网络
铅笔侠_小龙虾1 小时前
Flutter 实战: 计算器
开发语言·javascript·flutter
大模型玩家七七2 小时前
梯度累积真的省显存吗?它换走的是什么成本
java·javascript·数据库·人工智能·深度学习
. . . . .2 小时前
shadcn组件库
前端
2501_944711432 小时前
JS 对象遍历全解析
开发语言·前端·javascript