搞懂TypeScript中的类型断言和使用场景

简介: TypeScript 的类型断言看起来概念比较简单,但是对于刚接触 TypeScript 的使用者,可能对使用场景缺少认识,希望本文可以帮助你更了解类型断言。

后续也会更新TypeScript相关系列文章,欢迎持续关注

> TypeScript 的类型断言看起来概念比较简单,但是对于刚接触 TypeScript 的使用者,可能对使用场景缺少认识,希望本文可以帮助你更了解类型断言。


当你使用一个值,但是 TypeScript 不知道具体类型 或者 TypeScript 记录的类型没有办法满足使用要求时,可以使用类型断言来明确指定为自己想要使用的类型。


#### 语法:


类型断言有两种方式:


1.  使用 `<>` 语法

1.  使用 `as` 关键字


`<>` 会和 JSX 语法冲突,一般使用 `as` 。


我们来看几个类型断言的示例


1.对于通过标签获取的DOM,TypeScript可以推断出类型,但是对于其他方式,TypeScript无法推断,我们可以使用类型断言来明确指定元素类型。


```

const aEle = document.querySelector('a') // HTMLAnchorElement | null

const canvasEle = document.querySelector('#my_canvas') as HTMLCanvasElement

```

canvasEle变量也就有了HTMLCanvasElement类型,编辑器也会更好的进行代码提示和类型检查。



```

React.useEffect(() => {

 if (props.autoFocus) {

   const $this = ref.current as HTMLInputElement;

   ...

 }

}, []);

```


AntD中的示例:[ActionButton.tsx](https://github.com/ant-design/ant-design/blob/4.3.4/components/modal/ActionButton.tsx)


2.对于空对象占位,可以断言为特定类型,以获取正确的代码提示和类型推断


```

const [user, setUser] = useState<User | null>(null);

setUser(newUser);

const [user, setUser] = useState<User>({} as User);

setUser(newUser);

```


#### const 断言


`const` 断言告诉编译器为表达式推断出它能推断出的最窄或最特定的类型,而不是通用类型。


```

// point变成一个readonly 数组类型,修改数组内容会提示错误。

let point = [3, 4] as const; // readonly [3, 4]

point[0] = 1 // Error

```


我们来看一个代码示例:


```

function useDarkMode() {

 const [mode, setMode] = React.useState<'dark' | 'light'>(() => {

   // ...

   return 'light'

 })

 ...

 return [mode, setMode] as const

}

const [mode, setMode] = useDarkMode() // 伪代码,hook需要再函数组件中使用

```


我们来对比一下 mode 和 setMode 使用 as const 之后的差别:


在使用 const 断言之前,mode 和 setMode 类型为:


```

const mode: "dark" | "light" | React.Dispatch<React.SetStateAction<"dark" | "light">>

const setMode: "dark" | "light" | React.Dispatch<React.SetStateAction<"dark" | "light">>

```


![TypeScript中的类型断言](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1bc4c55517d1414e91aa853f6e2a6fe4~tplv-k3u1fbpfcp-zoom-1.image)


调用setMode时,会提示错误,因为 'dark' | 'light' 并不是可调用类型。


![TypeScript中的类型断言](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7f87ec09502b4787a342dd1d726ae5fd~tplv-k3u1fbpfcp-zoom-1.image)


使用 as const 断言之后,mode 和 setMode 类型为:


```

const mode: "dark" | "light"

const setMode: React.Dispatch<React.SetStateAction<"dark" | "light">>

```


![image-20220726173035919](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2ecdf62c1ec445d7910ddf28c1a03d26~tplv-k3u1fbpfcp-zoom-1.image)


![image-20220726173102524](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1247ef7a3006460685b3a708ea890d2d~tplv-k3u1fbpfcp-zoom-1.image)


调用传参错误时,也会有类型错误提示。


![image-20220726173131771](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5373890ad8eb40bab6ce7b2d1a626da3~tplv-k3u1fbpfcp-zoom-1.image)


可以看到,对于数组来说,每个元素的类型是整个数组元素类型的联合类型


```

const arr = [1,'2']

// const arr: (string | number)[]

```


使用 as const 断言之后,数组会变成 readonly 数组且**每个元素有了自己的特定类型**,也有了更好的错误提示。


再来看一个 rxjs 中的示例:[fromEvent.ts](https://github.com/ReactiveX/rxjs/tree/master/src/internal/observable/fromEvent.ts)


```

// These constants are used to create handler registry functions using array mapping below.

// 这些常量用于使用下面的数组映射创建处理程序注册表函数

const nodeEventEmitterMethods = ['addListener', 'removeListener'] as const;

const eventTargetMethods = ['addEventListener', 'removeEventListener'] as const;

const jqueryMethods = ['on', 'off'] as const;

```


使用 as const之后,类型检测更为严格:


-   readonly 数组,每个元素都有自己的字面量类型,无法调整为其他值,杜绝被意外修改的可能

-   在访问数组元素或进行数组解构时,因数组长度固定,避免越界,更不容易出错


const 断言和 typeof 搭配使用:[useSelection.tsx](https://github.com/ant-design/ant-design/blob/4.3.4/components/table/hooks/useSelection.tsx)


字符串使用 as const 之后,变量就有了字面量类型,typeof 操作符可以提取其字面量类型使用。


```

export const SELECTION_ALL = 'SELECT_ALL' as const;

export const SELECTION_INVERT = 'SELECT_INVERT' as const;

export type INTERNAL_SELECTION_ITEM =

 | SelectionItem

 | typeof SELECTION_ALL

 | typeof SELECTION_INVERT;

```


#### 规避类型检查


TypeScript **只允许类型断言为一个更具体或者更不具体的类型**,这个规则可以阻止一些错误的强制类型转换:


```

const x = "hello" as number;

// Error:将 'string' 类型转换为 'number' 类型可能是一个错误,因为这两种类型都没有充分重叠。如果这是故意的,请先将表达式转换为“unknown”。

```


我们再来看一个 Antd 中的使用示例: [back-top](https://github.com/ant-design/ant-design/blob/4.3.4/components/back-top/index.tsx)


```

React.useEffect(() => {

 bindScrollEvent();

 return () => {

   if (scrollEvent.current) {

     scrollEvent.current.remove();

   }

   (handleScroll as any).cancel();

 };

}, [props.target]);

```


handleScroll 是一个函数,但是其他文件中被增加了 cancel 属性,此处直接调用 cancel 方法, TypeScript会提示错误,可以断言为 any 来规避 TypeScript 的类型检查。


当然也可以使用命名空间为函数增加静态属性,类似:

```typescript

function buildLabel(name: string): string {

   return buildLabel.prefix + name + buildLabel.suffix;

}


namespace buildLabel {

   export let suffix = "";

   export let prefix = "Hello, ";

}


console.log(buildLabel("Sam Smith"));

```




#### 双重断言


对于我们已经明确的变量类型,如果不存在重叠,可以先断言为一个宽泛的类型(any、unknown),再断言为一个具体的类型。


```

// es default export should use const instead of let

const ExportTypography = (RefTypography as unknown) as React.FC<TypographyProps>;

```


[Typography](https://github.com/ant-design/ant-design/blob/4.3.4/components/typography/Typography.tsx)


**注意:类型断言只能够「欺骗」`TypeScript` 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误。当使用断言时,应该确保你了解当前值的类型,避免出错。对于可以收窄的类型,尽量使用类型守卫收窄而非断言。**


> TypeScript系列会更新多篇文章,欢迎关注。

相关文章
|
2月前
|
前端开发 JavaScript 安全
TypeScript在React Hooks中的应用:提升React开发的类型安全与可维护性
【7月更文挑战第17天】TypeScript在React Hooks中的应用极大地提升了React应用的类型安全性和可维护性。通过为状态、依赖项和自定义Hooks指定明确的类型,开发者可以编写更加健壮、易于理解和维护的代码。随着React和TypeScript的不断发展,结合两者的优势将成为构建现代Web应用的标准做法。
|
30天前
|
JavaScript
TypeScript——不能将类型“HTMLElement | null”分配给类型“HTMLElement”
TypeScript——不能将类型“HTMLElement | null”分配给类型“HTMLElement”
26 4
|
11天前
|
JavaScript 前端开发 编译器
Angular 与 TypeScript 强强联手太厉害啦!强类型编程带来巨大开发优势,快来一探究竟!
【8月更文挑战第31天】作为一名前端开发者,我致力于探索各种技术框架以提升开发效率与代码质量。近期深入研究了Angular与TypeScript的结合,体验到强类型编程带来的显著优势。Angular是一款强大的前端框架,而TypeScript则是由微软开发的一种强类型语言,为JavaScript增添了静态类型检查等功能。
20 0
|
1月前
|
JavaScript 编译器
typescript 解决变量多类型访问属性报错--工作随记
typescript 解决变量多类型访问属性报错--工作随记
|
21天前
|
JavaScript 前端开发 安全
TypeScript:解锁JavaScript的超级英雄模式!类型系统如何化身守护神,拯救你的代码免于崩溃与混乱,戏剧性变革开发体验!
【8月更文挑战第22天】TypeScript作为JavaScript的超集,引入了强大的类型系统,提升了编程的安全性和效率。本文通过案例展示TypeScript如何增强JavaScript:1) 显式类型声明确保函数参数与返回值的准确性;2) 接口和类加强类型检查,保证对象结构符合预期;3) 泛型编程提高代码复用性和灵活性。这些特性共同推动了前端开发的标准化和规模化。
43 0
|
29天前
|
JavaScript
TypeScript——Record类型
TypeScript——Record类型
31 0
|
1月前
|
JavaScript 前端开发 编译器
Typescript 回调函数、事件侦听的类型定义与注释--拾人牙慧
Typescript 回调函数、事件侦听的类型定义与注释--拾人牙慧
|
2月前
|
JavaScript 开发者 索引
TypeScript接口与类型别名:深入解析与应用实践
【7月更文挑战第10天】TypeScript的接口和类型别名是定义类型的关键工具。接口描述对象结构,用于类、对象和函数参数的形状约束,支持可选、只读属性及继承。类型别名则为复杂类型提供新名称,便于重用和简化。接口适合面向对象场景,类型别名在类型重用和复杂类型简化时更有优势。选择时要考虑场景和灵活性。
|
2月前
|
JavaScript 前端开发 程序员
Typescript 【实用教程】(2024最新版)含类型声明,类型断言,函数,接口,泛型等
Typescript 【实用教程】(2024最新版)含类型声明,类型断言,函数,接口,泛型等
53 0
|
2月前
|
JavaScript 安全
TypeScript(十一)泛型工具类型
TypeScript(十一)泛型工具类型
28 0