TypeScript中的类型断言

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

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

语法:

类型断言有两种方式:

  1. 使用 <> 语法
  2. 使用 as 关键字

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

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

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

const aEle = document.querySelector('a') // HTMLAnchorElement | null
const canvasEle = document.querySelector('#my_canvas') as HTMLCanvasElement
React.useEffect(() => {
  if (props.autoFocus) {
    const $this = ref.current as HTMLInputElement;
    ...
  }
}, []);

AntD中的示例: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中的类型断言

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

TypeScript中的类型断言

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

const mode: "dark" | "light"
const setMode: React.Dispatch<React.SetStateAction<"dark" | "light">>
image-20220726173035919
image-20220726173102524

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

image-20220726173131771

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

const arr = [1,'2'] 
// const arr: (string | number)[]

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

再来看一个 rxjs 中的示例: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

字符串使用 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

React.useEffect(() => {
  bindScrollEvent();
  return () => {
    if (scrollEvent.current) {
      scrollEvent.current.remove();
    }
    (handleScroll as any).cancel();
  };
}, [props.target]);

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

双重断言

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

// es default export should use const instead of let
const ExportTypography = (RefTypography as unknown) as React.FC<TypographyProps>;

Typography

注意:当使用断言时,应该确保你了解当前值的类型,避免出错。对于可以收窄的类型,尽量使用类型收窄而非断言。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容