本篇要來介紹 void 型別,原本也想把 undefined, null 一起寫在這篇,但怕篇幅過長還是拆開吧!不然閱讀量更低了
在 TypeScript 中,void
通常用於函式沒有回傳值的情況
在 JavaScript 中,函式如果沒有回傳值,會是 undefined
。然而,在 TypeScript 中,沒有回傳值不能寫undefined
,而是要寫 void
,void
與 undefined
並不相同
以下兩個函式的回傳值都會被推論為 void
function sayHi() { // : void
console.log('Hi!')
}
function noop() { // : void
return;
}
當有一個函式的回傳值被定義為 void,不代表這個函式就禁止不能回傳任何東西
換句話說,它還是可以 return 任何東西,只是會被「忽略」!!
type voidFunc = () => void;
const f1: voidFunc = () => {
return 'hello'; // ✅ Pass,但回傳值會被忽略
};
const f2: voidFunc = () => true; // ✅ Pass,但回傳值會被忽略
在上面的例子中,f1
, f2
都回傳了不同值,但是 TypeScript 會把回傳值忽略,所以並不會報錯
延續上方範例,將 f2()
的執行結果賦值 test
變數,test
它還是會被推論為 void
就算 f2
函式會回傳 true 也改變不了這個事實
所以這時如果想要使用 test,是會報錯的喔
因為我們沒有辦法使用和依賴一個會被忽略的值
const test = f2(); // : void
if (test) {
console.log('1') // ❌ An expression of type 'void' cannot be tested for truthiness.
}
我們知道 TypeScript 是 JavaScript 的超集,JavaScript 在語法上一般是允許函式回傳任何值的,即使你不打算使用它也是一樣。TypeScript 為了兼容這個行為,所以就算回傳在 void 的函式中去寫回傳值,也不會有問題,它只是在告訴編譯器說:「這個函式的回傳值不重要,可以忽略」
我們用以下這個範例來再次驗證
type voidFunc = () => void;
const f1: voidFunc = () => {
return 'hello';
};
console.log(f1()) // hello
這個例子印出來的結果是 hello
一開始有點困惑,不是回傳值會被忽略嗎?執行函式時還可以印出東西呀!🤔
後來了解到,能夠印出東西,代表函式有「正常執行」,而「回傳值被忽略」這件事本身並不會去影響函式的運行
TypeScript 中的 void 型別並不會改變 JavaScript 的行為
再來看看這個範例
const src = [1, 2, 3]; // : number[]
const dst = [0]; // : number[]
src.forEach((el) => dst.push(el));
forEach()
方法不會有回傳值,所以被推論為 void
即使在 callback 中使用了push()
,而 push()
會回傳一個數字(代表陣列的長度),但 TypeScript 也不會因為這個回傳值與 void 不符而報錯
這裡用利用不同範例來對照 void
型別的不同結果
我們上面提到回傳值被定義為 void
型別的函式,還是可以 return 任何東西,只是會被「忽略」!!
那這裡是在打自己臉嗎....?
function test1(): void {
return true; // ❌ Type 'boolean' is not assignable to type 'void'.
}
const test2 = function (): void {
return true; // ❌ Type 'boolean' is not assignable to type 'void'.
};
// ------------ 分隔線 ------------
type voidFunc = () => void;
const f1: voidFunc = () => {
return 'hello'; // ✅ Pass,但回傳值會被忽略
};
function test1
和 test2
都會報錯耶~
這是因為 TypeScript 對「函式字面量」和「函式型別」的處理方式不同
函式字面量
當使用 function
關鍵字來定義函式時,就被稱為「函式字面量」,範例中的 function test1(): void
就是,當你明確定義一個函式並指定回傳型別為 void 時,TypeScript 會嚴格檢查是否有回傳值,如果有就會報錯
函式型別(函式作為型別的一部分)
當你定義一個函式型別(如範例中的 type voidFunc = () => void
),TypeScript 並不會強制這個函式不能有回傳值,只是會把它忽略而已,就跟文章一開始所說的一樣
圖片來源
由圖可知,型別定義為 void
的變數可以接收 undefined
, any
, void
, never
的值,在 strictNullChecks 關閉(false) 的狀態下,還可以接收 null
型別
let undefinedVal: undefined;
let anyVal: any;
let nullVal: null;
let voidVal: void;
let neverVal: never = (() => { throw new Error('error') })()
let objectVal: object = { x: 'a'};
let unknownVal: unknown
let res1: void = undefinedVal; // ✅ Pass
let res2: void = anyVal; // ✅ Pass
let res3: void = nullVal; // 在 strictNullChecks 關閉(false)的狀態下才不會報錯
let res4: void = voidVal; // ✅ Pass
let res5: void = neverVal; // ✅ Pass
let res6: void = objectVal; // ❌ Type 'object' is not assignable to type 'void'.
let res7: void = unknownVal; // ❌ Type 'unknown' is not assignable to type 'void'.
在本文中,我們探討了 TypeScript 中的 void 型別、一些看似不合理但實際上是合乎邏輯的行為,以及它在型別系統中的應用
void 的用途:主要用於函式沒有回傳值的情況,當函式的回傳值為 void 時,TypeScript 允許函式回傳某些值,但會被忽略
函式字面量 vs 函式型別:當函式被定義為字面量(使用 function 關鍵字)並明確指定回傳 void 時,TypeScript 會嚴格檢查是否有回傳值,並且有則會報錯。而當使用函式型別(如 type voidFunc = () => void),即使回傳了值,TypeScript 也會接受並忽略這個值
void 的指派性:void 可以接受來自 undefined、any、void、never 的值,並且在 strictNullChecks 關閉的情況下,還能接受 null。然而,它不能接受 object 和 unknown 型別的值
每天的內容有推到 github 上喔
我觀察到閱讀量跟發文時間點關係蠻大的~
22:00~12:00 這區間出的文章閱讀量都會比較低
加油!
原來如此~我很常壓線發文哈哈🤣
謝謝你!一起加油~