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