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(); // 正确!
}
相关推荐
竹林8184 分钟前
在Web3前端用Node.js子进程批量校验钱包,我踩了这些性能与安全的坑
javascript·node.js
农夫山泉不太甜10 分钟前
Tauri v2 实战代码示例
前端
yuhaiqiang27 分钟前
被 AI 忽悠后,开始怀念搜索引擎了?
前端·后端·面试
红色石头本尊43 分钟前
1-umi-前端工程化搭建
前端
真夜1 小时前
关于对echart盒子设置百分比读取的宽高没有撑开盒子解决方案
前端
楠木6851 小时前
RAG 资料库 Demo 完整开发流程
前端·ai编程
肠胃炎1 小时前
挂载方式部署项目
服务器·前端·nginx
像我这样帅的人丶你还1 小时前
使用 Next.js + Prisma + MySQL 开发全栈项目
前端
FPGA小迷弟1 小时前
FPGA 时序约束基础:从时钟定义到输入输出延迟的完整设置
前端·学习·fpga开发·verilog·fpga
Kel1 小时前
深入剖析 openai-node 源码:一个工业级 TypeScript SDK 的架构之美
javascript·人工智能·架构