最近我开始学习使用 TypeScript。先看了看教程,好像很简单的样子,就那些类型嘛,定义好类型就完事了。但是实际使用中却遇到了不少问题,在这里记录一些经验。
1. keyof 的使用
当要使用中括号获取对象的属性时,我写了以下代码:
const a = { item1: 'xxx', item2: 'xxx' } function getValue(arg: string) { return a[arg] } getValue('item1')
getValue
的参数是字符串,类型就写成 string
,很合理嘛。但是报错了:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ item1: string; item2: string; }'.
类型'string'不能用作 a 的索引。我想不通,去问了别人,才明白 typescript 的意思是,arg
的类型应该是由对象的属性名组成的联合类型,如 arg: "item1" | "item2"
。把参数的类型改成 arg: keyof typeof a
就可以了。
keyof
操作符用于类型查询,返回类型的所有 key,组成联合类型。
2. ! 非空断言
我看的教程里有 as
断言,却没有讲非空断言 !
,我又是问了别人才知道。
如下 DOM 操作:
const a = document.querySelector('#a') a.parentNode.removeChild(a)
因为 querySelector
的返回值类型是 Element | null
,所以第二行就报错了:“对象可能为 "null"”
。
并且因为类型的原因,编辑器也不会提示 a 的属性和方法。这时候就要在值的后面加上感叹号,断言这是一个非空值,类型里就没有 null
了,只剩下 Element
类型。这样就正常了。
修改后的代码如下:
const a = document.querySelector('#a')! a.parentNode!.removeChild(a)
3. NodeListOf
有时候我们获取 DOM 元素时, ts 推断不出来具体的类型,只能给出笼统的 Element
类型,所以我们需要指定具体的类型。
同时我们可以配合 NodeListOf
指定 NodeList
集合,比如 document.querySelectorAll
的结果就是 NodeList
列表。
const list: NodeListOf<HTMLDivElement> = document.querySelectorAll('.xxx')
4. 索引签名
如下数组里包含了几个对象,他们的 key 和 value 都不一样,而且这个数组里有多少个对象也不确定,怎么定义它们呢?
const c = [{a:1}, {b: 2}, {c: "3"}]; // 定义类型 interface Arrayc { [key: string]: number | string; }
另一个例子:
interface Test { imgNumberPerWork: number displayCover: boolean [key: string]: number | boolean } const test: Test = { imgNumberPerWork: 0, displayCover: true } function setValue(key: keyof Test, val: any) { test[key] = val } setValue('imgNumberPerWork', 1)
变量 test
里有不同类型的值,当用 test[key]
赋值的时候,就需要 [key: string]: number | boolean
这个字符串索引签名。
索引签名的前半部分 [key: string]
接收指定类型的索引名(这里是 string),后半段表示这样的索引会返回什么类型的值(这里是 number | boolean)
。
如果给索引签名前面加上 readonly
,那么它所表示的索引就是只读的,不能赋值了。
5. 指定 this 的类型
如果我们的函数中使用了 this,通常 typescript 都会报错:
"this" 隐式具有类型 "any",因为它没有类型注释。
一个很好的办法是,把 this 作为函数的第一个参数,指定类型。如下:
function xzTip(this:HTMLElement, arg: XzTipArg) { const tipText = this.dataset.tip }
当编译 ts 文件的时候,typescript 会自动的去掉这个 this 参数,不影响 js 的运行。
6. 循环 Set 结构
如下错误代码:
let set = new Set() set.add(1) for (const val of set.values()) { // ... }
set.values()
报错:
类型“IterableIteratorShim
因为 for of
循环的对象需要具有 iterator
,Set
结构自带 iterator
,直接循环 set
,不需要 .values()
。
set.values()
其实就是 set
的 iterator
,如果循环它,就相当于去循环对象的遍历器,而不是循环对象本身,所以报错。但这样的代码在 JavaScript 里是可以正常运行的,可怕。
7. 设置 Promise 的类型
const p = new Promise<Blob>((resolve,reject)=>{ resolve() })
如果 Promise
后面没有加
,那么默认的返回值就是 Promise
。像上面那样就可以指定返回值为 Promise
了。
虽然看起来很简单,但我一开始不知道是这样写的,瞎JB试了半天,浪费了不少时间。
TypeScript 使用记录
-
Google Chrome 81Mac OS X 10.15.2 -
Google Chrome 76Windows 第五个学到了,上次在用ts写webpack loader的时候出现过这个问题,咱也不知道为什么这样做就行了,咱也不敢问(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄
2. non-null assertion 还是很危险的
5. 第五个真是学到了 是时候好好攻读官方 handbook 了, 感谢
7. 似乎想写 blob 写成 promise 了?