saber酱的抱枕

生于忧患,死于安乐

09/1
14:00
学习

TypeScript 使用记录

最近我开始学习使用 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”必须具有返回迭代器的 "[Symbol.iterator]()" 方法。

因为 for of 循环的对象需要具有 iteratorSet 结构自带 iterator,直接循环 set,不需要 .values()

set.values() 其实就是 setiterator,如果循环它,就相当于去循环对象的遍历器,而不是循环对象本身,所以报错。但这样的代码在 JavaScript 里是可以正常运行的,可怕。

7. 设置 Promise 的类型

const p = new Promise<Blob>((resolve,reject)=>{
  resolve()
})

如果 Promise 后面没有加 ,那么默认的返回值就是 Promise。像上面那样就可以指定返回值为 Promise 了。

虽然看起来很简单,但我一开始不知道是这样写的,瞎JB试了半天,浪费了不少时间。

TypeScript 使用记录

    1. saber 文章作者
      Google Chrome 81Google Chrome 81WindowsWindows

      是的,非空断言容易导致出错
      this 那个,其实如果是使用了 class 是可以推断 this 的,如果没有 class 直接定义的函数,可能就需要指定 this
      第五个其实是 promise blob,有些标签被吞掉了

      回复