TS中關于void類型的奇怪現象,你知道嗎?
前言
在TS中有一種類型為void ,它表示的是空,但是需要注意的是它與JS中的空并不是一回事。
并且它一般用于給函數返回值聲明類型 ,雖然也可以把一個變量的類型聲明為void,但我們一般不會這么干,因為沒有意義,為什么這么說呢?在下面的例子中來解答這個問題。
void類型
給變量聲明void類型
let name: void // 聲明一個變量name,類型為void
name = 'nanjiu' // 報錯 不能將類型“string”分配給類型“void”。
name = 18 // 報錯 不能將類型“number”分配給類型“void”。
name = null // 報錯 不能將類型“null”分配給類型“void”。
name = undefined // 正常
圖片
也就是說當類型為void時,它能夠接受的值就只有一個:undefined,其它任何值都不行。
現在是不是能夠解釋為什么我們一般不會給變量聲明為void類型了,因為它的值只能是undefined,而undefined在我們實際開發時并沒有任何意義。
函數返回值聲明為void類型
顯式返回
當給函數返回值類型申明為void時,我們可以在函數中return一個undefined
function sayHello(): void {
console.log('hello')
return undefined
}
const str = sayHello()
console.log(str) // undefined
除undefined之外,return其它任何值都不可以。
隱式返回
在JS中,當我們沒有在函數中顯式地返回一個值時,它也會有一個隱式的返回值,而這個返回值恰好就是undefined,也就是說下面這種寫法也是合理的
function sayHello(): void {
console.log('hello')
}
const str = sayHello()
console.log(str) // undefined
不應該依賴void值
void還有一個特點就是,調用者不應該依賴該返回值進行任何操作!!!
比如:
let name: void // 聲明一個變量name,類型為void
// 函數返回值類型為void
function sayHello(): void {
console.log('hello')
}
// 函數返回值類型為void,值為undefined
const str = sayHello()
console.log(str) // undefined
// 報錯 無法測試 "void" 類型的表達式的真實性。
if(str) {
console.log('str存在')
} else {
console.log('str不存在')
}
圖片
此時你會發現,vscode直接報錯了,void在TS中的含義就是空,表示什么也沒有,你就不應該使用它來進行任何操作。
總結
- void一般用來聲明函數返回值的類型,它的含義為空,它能夠接受的值只有一個:undefined
- 我們不應該依賴void類型的返回值進行任何操作
其實很簡單,總結來說就兩點,但是下面的例子你可能會有點吃驚...
type
簡單介紹一下type,它是TS中創建自定義類型的一個關鍵字。它可以為任意類型創建別名,方便進行類型復用與擴展
比如:
// 創建一個自定義類型,可以是字符串或者數字
type strOrnum = string | number
// 聲明一個變量,類型為 strOrnum
let str: strOrnum
str = 'nanjiu' // 可以賦值為字符串
str = 18 // 也可以賦值為數字
當然type還有很多強大的功能:聯合類型、交叉類型等。本文暫不介紹,我們來看一個有趣的問題:
為函數聲明類型
// 創建一個函數類型,參數為string類型,返回值為void
type say = (name: string) => void
// 定義一個函數,類型為say
let sayHello: say = (name: string) => {
console.log(`hello ${name}`)
}
sayHello('nanjiu')
上面通過type創建了一個函數類型:該函數有一個參數,并且參數類型為string。函數的返回值類型為void
函數返回非undefined值
從上面void的介紹中,我們可以確定該函數的返回值只能為undefined(顯式隱式都可以)。
但是此時卻不是這樣了,你給它返回任何值都可以...
// 創建一個函數類型,參數為string類型,返回值為void
type say = (name: string) => void
// 定義一個函數,類型為say
let sayHello: say = (name: string) => {
console.log(`hello ${name}`)
return null
}
const res = sayHello('nanjiu')
console.log(res) // null
為什么會這樣?
上面這個例子是不是違背了當初void的定義,難道這是TS的bug嗎?
其實并不是的,官方的解釋是:
是為了確保如下代碼成立,我們知道Array.prototype.push的返回值是一個數字,而Array.prototype.forEach方法期望其回調的返回類型是void
const arr = [1, 2, 3, 4, 5]
const list = [0]
arr.forEach(item => list.push(item))
console.log(list)
圖片
紅色框圈出來的是forEach的回調函數的類型定義,也就是item => list.push(item)
它也就相當于是使用type進行的自定義類型聲明
type callbackfn = (value: number, index: number, array: number[]) => void
該函數的類型定義為,有三個參數,前兩個類型均為number,第三個參數為全為number類型的數組,函數返回值類型為void
由于我們的回調函數使用的是箭頭函數的簡寫形式,該簡寫形式相當于會return list.push(item),并且push方法又是有返回值的
item => list.push(item)
等同于
item => {
return list.push(item)
}
等同于
item => {
return 2
// return 3
// return 4
// ...
}
那也就是說該函數的返回值類型變成了number,不符合void的類型定義。
所以TS官方為了讓我們能夠使用這種簡寫形式,才有了這一現象。
使用類型聲明限制函數返回值為void時,TS并不會嚴格要求函數返回空
否則的話這種場景我們就只能這樣寫了:
arr.forEach(item => {
list.push(item)
})
// 或者
arr.forEach(function(item) {
list.push(item)
})
但需要注意的是,盡管使用類型聲明限制函數返回值為void時,TS并不會嚴格要求函數返回空,但我們還是不能依賴其返回值進行任何操作
// 創建一個函數類型,參數為string類型,返回值為void
type say = (name: string) => void
// 定義一個函數,類型為say
let sayHello: say = (name: string) => {
console.log(`hello ${name}`)
return name
}
const res = sayHello('nanjiu')
console.log(res) // nanjiu
if (res) {
console.log('res')
}