跳至主要內容

[Easy] 基本型別與型別註解

1. What are TypeScript Basic Types?

TypeScript 的基本型別有哪些?

TypeScript 提供了多種基本型別,用於定義變數、函式參數和回傳值的型別。

基本型別

// 1. number:數字(整數、浮點數)
let age: number = 30;
let price: number = 99.99;

// 2. string:字串
let name: string = 'John';
let message: string = `Hello, ${name}!`;

// 3. boolean:布林值
let isActive: boolean = true;
let isCompleted: boolean = false;

// 4. null:空值
let data: null = null;

// 5. undefined:未定義
let value: undefined = undefined;

// 6. void:無回傳值(主要用於函式)
function logMessage(): void {
console.log('Hello');
}

// 7. any:任意型別(應避免使用)
let anything: any = 'hello';
anything = 42;
anything = true;

// 8. unknown:未知型別(比 any 更安全)
let userInput: unknown = 'hello';
// userInput.toUpperCase(); // ❌ 錯誤:需要先檢查型別

// 9. never:永遠不會發生的值(用於永遠不會回傳的函式)
function throwError(): never {
throw new Error('Error');
}

// 10. object:物件(不常用,建議使用介面)
let user: object = { name: 'John' };

// 11. array:陣列
let numbers: number[] = [1, 2, 3];
let names: Array<string> = ['John', 'Jane'];

// 12. tuple:元組(固定長度和型別的陣列)
let person: [string, number] = ['John', 30];

2. Type Annotations vs Type Inference

型別註解 vs 型別推斷

型別註解(Type Annotations)

定義:明確指定變數的型別。

// 明確指定型別
let age: number = 30;
let name: string = 'John';
let isActive: boolean = true;

// 函式參數和回傳值
function add(a: number, b: number): number {
return a + b;
}

型別推斷(Type Inference)

定義:TypeScript 根據初始值自動推斷型別。

// TypeScript 自動推斷為 number
let age = 30; // age: number

// TypeScript 自動推斷為 string
let name = 'John'; // name: string

// TypeScript 自動推斷為 boolean
let isActive = true; // isActive: boolean

// 函式回傳值也會自動推斷
function add(a: number, b: number) {
return a + b; // 自動推斷回傳值為 number
}

何時使用型別註解

需要明確指定型別的情況

// 1. 變數宣告時沒有初始值
let value: number;
value = 10;

// 2. 函式參數(必須指定)
function greet(name: string): void {
console.log(`Hello, ${name}!`);
}

// 3. 函式回傳值(建議明確指定)
function calculate(): number {
return 42;
}

// 4. 複雜型別,推斷可能不準確
let data: { name: string; age: number } = {
name: 'John',
age: 30,
};

3. Common Interview Questions

常見面試題目

題目 1:型別推斷

請說明以下程式碼中每個變數的型別。

let value1 = 10;
let value2 = 'hello';
let value3 = true;
let value4 = [1, 2, 3];
let value5 = { name: 'John', age: 30 };
點擊查看答案
let value1 = 10;                    // number
let value2 = 'hello'; // string
let value3 = true; // boolean
let value4 = [1, 2, 3]; // number[]
let value5 = { name: 'John', age: 30 }; // { name: string; age: number }

解釋

  • TypeScript 會根據初始值自動推斷型別
  • 陣列推斷為元素型別的陣列
  • 物件推斷為物件的結構型別

題目 2:型別錯誤

請找出以下程式碼中的型別錯誤。

let age: number = 30;
age = 'thirty';

let name: string = 'John';
name = 42;

let isActive: boolean = true;
isActive = 'yes';

let numbers: number[] = [1, 2, 3];
numbers.push('4');
點擊查看答案
let age: number = 30;
age = 'thirty'; // ❌ Type 'string' is not assignable to type 'number'

let name: string = 'John';
name = 42; // ❌ Type 'number' is not assignable to type 'string'

let isActive: boolean = true;
isActive = 'yes'; // ❌ Type 'string' is not assignable to type 'boolean'

let numbers: number[] = [1, 2, 3];
numbers.push('4'); // ❌ Argument of type 'string' is not assignable to parameter of type 'number'

正確寫法

let age: number = 30;
age = 30; // ✅

let name: string = 'John';
name = 'Jane'; // ✅

let isActive: boolean = true;
isActive = false; // ✅

let numbers: number[] = [1, 2, 3];
numbers.push(4); // ✅

題目 3:any vs unknown

請說明 anyunknown 的差異,並說明應該使用哪一個。

// 情況 1:使用 any
function processAny(value: any): void {
console.log(value.toUpperCase()); // ?
}

// 情況 2:使用 unknown
function processUnknown(value: unknown): void {
console.log(value.toUpperCase()); // ?
}
點擊查看答案

情況 1:使用 any

function processAny(value: any): void {
console.log(value.toUpperCase()); // ⚠️ 編譯通過,但執行時可能出錯
}

processAny('hello'); // ✅ 正常執行
processAny(42); // ❌ 執行時錯誤:value.toUpperCase is not a function

情況 2:使用 unknown

function processUnknown(value: unknown): void {
// console.log(value.toUpperCase()); // ❌ 編譯錯誤:Object is of type 'unknown'

// 需要先檢查型別
if (typeof value === 'string') {
console.log(value.toUpperCase()); // ✅ 安全
}
}

差異比較

特性anyunknown
型別檢查完全關閉需要先檢查才能使用
安全性不安全安全
使用建議避免使用推薦使用

最佳實踐

// ✅ 推薦:使用 unknown,然後進行型別檢查
function processValue(value: unknown): void {
if (typeof value === 'string') {
console.log(value.toUpperCase());
} else if (typeof value === 'number') {
console.log(value.toFixed(2));
}
}

// ❌ 避免:使用 any
function processValue(value: any): void {
console.log(value.toUpperCase()); // 不安全
}

題目 4:陣列型別

請說明以下陣列型別宣告的差異。

let arr1: number[];
let arr2: Array<number>;
let arr3: [number, string];
let arr4: any[];
點擊查看答案
// 1. number[]:數字陣列(推薦寫法)
let arr1: number[] = [1, 2, 3];
arr1.push(4); // ✅
arr1.push('4'); // ❌ 錯誤

// 2. Array<number>:泛型陣列(與 number[] 等價)
let arr2: Array<number> = [1, 2, 3];
arr2.push(4); // ✅
arr2.push('4'); // ❌ 錯誤

// 3. [number, string]:元組(Tuple)- 固定長度和型別
let arr3: [number, string] = [1, 'hello'];
arr3[0] = 2; // ✅
arr3[1] = 'world'; // ✅
arr3[2] = true; // ❌ 錯誤:長度超過 2
arr3.push('test'); // ⚠️ TypeScript 允許,但不建議

// 4. any[]:任意型別陣列(不推薦)
let arr4: any[] = [1, 'hello', true];
arr4.push(42); // ✅
arr4.push('world'); // ✅
arr4.push(false); // ✅(但失去型別檢查)

使用建議

  • 一般陣列:使用 number[]Array<number>
  • 固定結構:使用元組 [type1, type2]
  • 避免使用 any[],優先使用具體型別或 unknown[]

題目 5:void vs never

請說明 voidnever 的差異和使用場景。

// 情況 1:void
function logMessage(): void {
console.log('Hello');
}

// 情況 2:never
function throwError(): never {
throw new Error('Error');
}

function infiniteLoop(): never {
while (true) {
// 無限迴圈
}
}
點擊查看答案

void

  • 用途:表示函式沒有回傳值
  • 特點:函式可以正常結束,只是不回傳值
  • 使用場景:事件處理函式、副作用函式
function logMessage(): void {
console.log('Hello');
// 函式正常結束,不回傳值
}

function onClick(): void {
// 處理點擊事件,不需要回傳值
}

never

  • 用途:表示函式永遠不會正常結束
  • 特點:函式會拋出錯誤或進入無限迴圈
  • 使用場景:錯誤處理、無限迴圈、型別守衛
function throwError(): never {
throw new Error('Error');
// 永遠不會執行到這裡
}

function infiniteLoop(): never {
while (true) {
// 永遠不會結束
}
}

// 型別守衛中使用
function assertNever(value: never): never {
throw new Error(`Unexpected value: ${value}`);
}

差異比較

特性voidnever
函式結束正常結束永遠不會結束
回傳值undefined沒有回傳值
使用場景無回傳值的函式錯誤處理、無限迴圈

4. Best Practices

最佳實踐

推薦做法

// 1. 優先使用型別推斷
let age = 30; // ✅ 讓 TypeScript 推斷
let name = 'John'; // ✅

// 2. 函式參數和回傳值明確指定型別
function calculate(a: number, b: number): number {
return a + b;
}

// 3. 使用 unknown 而非 any
function processValue(value: unknown): void {
if (typeof value === 'string') {
console.log(value.toUpperCase());
}
}

// 4. 使用具體的陣列型別
let numbers: number[] = [1, 2, 3]; // ✅
let names: Array<string> = ['John', 'Jane']; // ✅

// 5. 使用元組表示固定結構
let person: [string, number] = ['John', 30]; // ✅

避免的做法

// 1. 避免使用 any
let value: any = 'hello'; // ❌

// 2. 避免不必要的型別註解
let age: number = 30; // ⚠️ 可以簡化為 let age = 30;

// 3. 避免使用 object 型別
let user: object = { name: 'John' }; // ❌ 使用介面更好

// 4. 避免混合型別陣列(除非必要)
let mixed: (string | number)[] = ['hello', 42]; // ⚠️ 考慮是否真的需要

5. Interview Summary

面試總結

快速記憶

基本型別

  • number, string, boolean, null, undefined
  • void(無回傳值), never(永遠不回傳)
  • any(任意型別,避免使用), unknown(未知型別,推薦使用)

型別註解 vs 推斷

  • 型別註解:明確指定 let age: number = 30
  • 型別推斷:自動推斷 let age = 30

陣列型別

  • number[]Array<number>:一般陣列
  • [number, string]:元組(固定結構)

面試回答範例

Q: TypeScript 有哪些基本型別?

"TypeScript 提供了多種基本型別,包括 number、string、boolean、null、undefined。還有一些特殊型別:void 表示無回傳值,主要用於函式;never 表示永遠不會發生的值,用於永遠不會回傳的函式;any 是任意型別,但應該避免使用;unknown 是未知型別,比 any 更安全,需要先檢查型別才能使用。另外還有陣列型別 number[] 和元組型別 [number, string]。"

Q: any 和 unknown 的差異是什麼?

"any 會完全關閉型別檢查,可以直接使用任何屬性或方法,但這是不安全的。unknown 則需要先進行型別檢查才能使用,更安全。例如使用 unknown 時,需要先用 typeof 檢查型別,確認後才能呼叫對應的方法。建議優先使用 unknown 而非 any。"

Reference