變數和常數

`let`變數

```{
let a = 5;
console.log(a);
}

console.log(a);```

```let a = 1;

{
let a = 5;
console.log(a);
}

console.log(a);```

5
1

5
5

```let a = 1;

let a = 5;

console.log(a);```

`let`變數的遮蔽可以使用不同型別，例如以下程式是合法的：

```let a = 1;

{
let a = '5';
console.log(a);
}

console.log(a);```

```let a;

console.log(a);

a = 5;

console.log(a);```

undefined
5

```let a: number;

console.log(a);

a = 5;

console.log(a);```

```let a: number | undefined;

console.log(a);

a = 5;

console.log(a);```

`const`常數

```const a;

console.log(a);```

```const a = 1;

a = 2;

console.log(a);```

```const a = {
f: 1
};

a.f = 2;

console.log(a.f);```

2

`var`變數

TypeScript中，用`var`關鍵字來宣告變數的語法和用`let`關鍵字是一樣的。最大的差別在於`var`變數的scope是在其被宣告出來的同一個函數主體之後。例如：

```{
var a = 5;
console.log(a);
}

console.log(a);```

5
5

```function f() {
var a = 5;
console.log(a);
}

console.log(a);```

```{
console.log(a);
var a = 5;
}

console.log(a);```

undefined
5

```var a = 1;
var a = 2;

console.log(a);```

2

```var a = 1;

{
var a = 5;
console.log(a);
}

console.log(a);```

5
5

```var a = 1;
a = 2;

console.log(a);```
```var a = 1;

{
a = 5;
console.log(a);
}

console.log(a);```

```var a = 1;

{
var a = "5";
console.log(a);
}

console.log(a);```

```var a = 1;

{
let a = "5";
console.log(a);
}

console.log(a);```

5
1

```var a = 1;

function f() {
var a = "5";
console.log(a);
}

f();

console.log(a);```

5
1

```var a: number | undefined;

console.log(a);

a = 5;

console.log(a);```

undefined
5

在宣告時未定義初始值的變數型別

```let a = 1;
a = "5";```

```let a;

a = 1;
a = "5";```

```let a: number | string;

a = 1;
a = "5";```

```let a: number | string | boolean | object;

a = 1;
a = "5";
a = true;
a = {};```

```let a: number | string | boolean | object | undefined;

let b = a;

a = 1;
a = "5";
a = true;
a = {};```

資料型別

TypeScript是「靜態型別」(Static Typing)的程式語言，在編譯階段就要完全決定好變數的型別。在許多情況下，`tsc`會替我們推論出變數的型別，使我們不需要明確定義出來。例如：

```let a;

a = 1;
a = "5";

let b = false;```

```function f(x) {

}```

```function f(x: any) {

}```

```function f(x: any) {
if (typeof x === "number") {
return 1;
} else {
return "something";
}
}```

```function f(x: any): number | string {
if (typeof x === "number") {
return 1;
} else {
return "something";
}
}```

```function f(x: any) {

}```

```function f(x: any): void {

}```

基本資料型別(Primitive Data Types)

JavaScript程式語言中一共有七種基本資料型別，分別是數值(`number`)、布林(`boolean`)、字串(`string`)、符號(`symbol`)、任意精度整数(`bigint`)、Null(`null`)和Undefined(`undefined`)，底下將分別介紹這七種型別。

數值(`number`)

`number`型別採用64位元的IEEE 754標準來表示整數和浮點數數值。其中整數的安全範圍在`-(253 - 1)` ~ `253 - 1`之間。我們可以透過`Number.MIN_SAFE_INTEGER``Number.MAX_SAFE_INTEGER`來得到其所能表示的整數最小值與最大值。

```console.log(Number.MAX_SAFE_INTEGER);
console.log(Number.MIN_SAFE_INTEGER);```

```console.log(Number.MAX_VALUE);
console.log(Number.MIN_VALUE);```

JavaScript允許加上底線`_`來分割太多位數的數值。例如：

`let x = 12_345;`

```let h = 0xff;
let o = 0o377;
let b = 0b11111111;```

```let a = NaN;

if (isNaN(a)) {
console.log("Equal!");
}```

```let a = Infinity - 99999;

if (a === Infinity + 99999) {
console.log("Equal!");
}```

字串(`string`)

JavaScript的字串在內部是用UTF-16的編碼格式來儲存的，所以Unicode能支援的字元，都可以被用在JavaScript的字串當中。字串的型別名稱為`string`。這個型別我們在之前就有用到了，在此只再補充兩點。

```let major = 1;
let minor = 0;
let patch = 0;

let version = major + "." + minor + "." + patch;

console.log(version);```

1.0.0

```let major = 1;
let minor = 0;
let patch = 0;

let version = `\${major}.\${minor}.\${patch}`;

console.log(version);```

```let s = "2.75";

let i = parseInt(s);
let f = parseFloat(s);

console.log(i);
console.log(f);```

2
2.75
符號(`symbol`)

`symbol`是ES6後才被加進JavaScript中的新型別。它的樣子如下：

```let a: symbol = Symbol(undefined);
let b: symbol = Symbol("description");
let c: symbol = Symbol(1);```

`Symbol`是一個JavaScript內建，用來產生並回傳`symbol`型別的值的函數。它有一個參數，這個參數的型別為`number | string | undefined`，為了讓方便開發者能區分不同的`symbol`型別的值。不管這個參數被代進什麼值，建立出來的兩個`symbol`型別的值都是不相等的。例如：

```let a: symbol = Symbol(undefined);
let b: symbol = Symbol(undefined);
let c: symbol = Symbol(1);
let d: symbol = Symbol(1);

if (a === b) {
console.log("a, b are equal!");
}

if (c === d) {
console.log("c, d are equal!");
}```

任意精度整数(`bigint`)

`bigint`是未來才會被正式加進JavaScript中的新型別，它可以解決JavaScript的`number`型別只能表示54位元整數的問題，因為`bigint`能夠表示的整數範圍完全沒有限制！想要在TypeScript使用`bigint`，編譯目標必須要改成`ES2020`之後的版本。

`let i: bigint = 633825300114114700748351602688n;`

`number`型別能用的運算子，除了`>>>`(無號向右位移)外，`bigint`也都可以用。

Null(`null`)

`null`值的型別即為`null`。在預設的情況下，`null`是任意型別的子型別，所以任意型別都可以使用`null`值。例如：

```function f1(): number {
return null;
}

function f2(): boolean {
return null;
}

function f3(): string {
return null;
}```

```function f1(): number | null {
return null;
}

function f2(): boolean | null {
return null;
}

function f3(): string | null {
return null;
}```
Undefined(`undefined`)

`undefined`值的型別即為`undefined`。在預設的情況下，`undefined`是任意型別的子型別，所以任意型別都可以使用`undefined`值。例如：

```function f1(): number {
return undefined;
}

function f2(): boolean {
return undefined;
}

function f3(): string {
return undefined;
}```

```function f1(): number | undefined {
return undefined;
}

function f2(): boolean | undefined {
return undefined;
}

function f3(): string | void {
return undefined;
}```

```function f1(): number | undefined | null | void {
return 1;
}

const r1: number = f1()!;```

陣列

`let a = [2, 4, 6, 8, 10];`

`let a: number[] = [2, 4, 6, 8, 10];`

```let a: number[] = [2, 4, 6, 8, 10];

a[2] = a[1] + a[0];```

```let a: number[] = [2, 4, 6, 8, 10];

let b: number = a[6];

console.log(b);```

```let a: number[] = [2, 4, 6, 8, 10];

console.log(a.length);```

```let a: number[] = [2, 4, 6, 8, 10];

a.push(12);```

```let a: number[] = [2, 4, 6, 8, 10];

if (Array.isArray(a)) {
console.log(a[0]);
}```

`==`和`===`

JavaScript的「相等」運算子有`==``===`兩種，前者(兩個等於)會自動將兩個運算元的值轉成相同型別，然後去判斷值是否相同，而後者(三個等於)則會判斷型別和值是否都相同，所以後者也會算得比前者還要來得快。

```let a: number | string = 5;
let b: number | string = 0;

if (1) {
b = "5";
}

console.log(a == b);
console.log(a === b);```

true
false

```console.log(5 == "5");
console.log(5 === "5");```

函數

```function save(path: string, data: string, force: boolean): boolean {
const fs = require("node:fs");

if (fs.existsSync(path)) {
if (force) {
} else {
return false;
}
}

fs.writeFileSync(path, data);

return true;
}```

```function save(path: string, data: string, force: boolean = false): boolean {
const fs = require("node:fs");

if (fs.existsSync(path)) {
if (force) {
} else {
return false;
}
}

fs.writeFileSync(path, data);

return true;
}```

```function save(path: string, data: string, force?: boolean): boolean {
const fs = require("node:fs");

if (fs.existsSync(path)) {
if (force) {
} else {
return false;
}
}

fs.writeFileSync(path, data);

return true;
}```

```function save(path: string, data: string = "Hello, world!", force: boolean = false): boolean {
const fs = require("node:fs");

if (fs.existsSync(path)) {
if (force) {
} else {
return false;
}
}

fs.writeFileSync(path, data);

return true;
}```

`save("123", true);`

`save("123", undefined, true);`

```function save(path: string, data: string, force: boolean = false): boolean {
const fs = require("node:fs");

if (fs.existsSync(path)) {
if (force) {
} else {
return false;
}
}

fs.writeFileSync(path, data);

return true;
}

function save(path: string, force: boolean = false): boolean {
const fs = require("node:fs");

if (fs.existsSync(path)) {
if (force) {
} else {
return false;
}
}

fs.writeFileSync(path, "Hello, world!");

return true;
}```

```function save(path: string, data_force?: string | boolean, force?: boolean): boolean {
let data;

const t_data_force = typeof data_force;

if (t_data_force === "undefined") {
data = "Hello, world!";
force = false;
} else if (t_data_force === "boolean") {
data = "Hello, world!";
force = data_force;
} else { // string
data = data_force;
}

const fs = require("node:fs");

if (fs.existsSync(path)) {
if (force) {
} else {
return false;
}
}

fs.writeFileSync(path, data);

return true;
}```

```function save(path: string, data_force?: string | boolean, force?: boolean): boolean {
let data;

const t_data_force = typeof data_force;

if (t_data_force === "undefined") {
data = "Hello, world!";
force = false;
} else if (t_data_force === "boolean") {
data = "Hello, world!";
force = data_force as boolean;
} else { // string
data = data_force;
}

const fs = require("node:fs");

if (fs.existsSync(path)) {
if (force) {
} else {
return false;
}
}

fs.writeFileSync(path, data);

return true;
}```

```function save(path: string): boolean;
function save(path: string, data: string): boolean;
function save(path: string, data: string, force: boolean): boolean;
function save(path: string, force: boolean): boolean;
function save(path: string, data_force?: string | boolean, force?: boolean): boolean {
let data;

const t_data_force = typeof data_force;

if (t_data_force === "undefined") {
data = "Hello, world!";
force = false;
} else if (t_data_force === "boolean") {
data = "Hello, world!";
force = data_force as boolean;
} else { // string
data = data_force;
}

const fs = require("node:fs");

if (fs.existsSync(path)) {
if (force) {
} else {
return false;
}
}

fs.writeFileSync(path, data);

return true;
}

save("/path/to/file", true, false); // compilation error```

```function add(a: number, b: number): number {
return a + b;
}

const subtract = function (a: number, b: number): number {
return a - b;
};

function math(a: number, b: number, f: Function): number {
return f(a, b);
}

3
1

```function math(a: number, b: number, f: Function): number {
return f(a, b);
}

let answer = math(2, 1, (a: number, b: number) => {
return a + b;
});

answer = math(2, 1, (a: number, b: number) => {
return a - b;
});

```function math(a: number, b: number, f: Function): number {
return f(a, b);
}

let answer = math(2, 1, (a: number, b: number) => a + b);

answer = math(2, 1, (a: number, b: number) => a - b);

```function math(a: number, b: number, f: (a: number, b: number) => number): number {
return f(a, b);
}

let answer = math(2, 1, (a, b) => a + b);

answer = math(2, 1, (a, b) => a - b);

```function math(f: (...numbers: number[]) => number, ...numbers: number[]): number {
return f(...numbers); // logically equals to `return f(numbers[0], numbers[1], numbers[2], ..., numbers[numbers.length - 1]);`
}

let answer = math((...numbers) => numbers.reduce((a, b) => a + b), 100, 50, 10, 2);

answer = math((...numbers) => numbers.reduce((a, b) => a - b), 100, 50, 10, 2);

162
38

```function math(f: (...numbers: number[]) => number, ...numbers: number[]): number {
return f(numbers);
}

let answer = math((...numbers) => numbers.reduce((a, b) => a + b), 100, 50, 10, 2);

answer = math((...numbers) => numbers.reduce((a, b) => a - b), 100, 50, 10, 2);

註解

TypeScript程式語言的單行註解都是由`//`開頭，直到該行結束。例如：

```// So we're doing something complicated here, long enough that we need
// multiple lines of comments to do it! Whew! Hopefully, this comment will
// explain what's going on.```

```/*
So we're doing something complicated here, long enough that we need
multiple lines of comments to do it! Whew! Hopefully, this comment will
explain what's going on.
*/```

```{
"compilerOptions": {
...

```
}
...
}```

流程控制

if條件判斷語法

`if``else`關鍵字組成的條件判斷語法我們已經會用了，這邊要再多提一個三元條件運算的語法，可以用來精簡程式。

```if (條件表達式) {
條件表達式的值不為undefined、null、false、0或NaN時執行的程式敘述區塊
} else {
條件表達式的值為undefined、null、false、0或NaN時執行的程式敘述區塊
}```

`條件表達式 ? 條件表達式的值不為undefined、null、false、0或NaN時執行的表達式 : 條件表達式的值為undefined、null、false、0或NaN時執行的表達式`

```let y: number;

...

let x;

if (y >= 0) {
x = y;
} else {
x = -y;
}```

```let y = 0;

...

let x = y >= 0 ? y : -y;```

switch判斷語法

```let mode: number;

if (mode === 0) {
// do something
} else if (mode === 1) {
// do something
} else if (mode === 2) {
// do something
} else if (mode === 3 || mode === 4) {
if (mode === 3) {
// do something
}

// do something
} else {
// do something
}```

```switch (mode) {
case 0:
// do something
break;
case 1:
// do something
break;
case 2:
// do something
break;
case 3:
// do something
case 4:
// do something
break;
default:
// do something
}```

`switch`關鍵字後必須要接上一對小括號`()`，括號中間必須要放入一個表達式。在這對`小括號`的後面，要再接上一對大括號`{}`作為switch判斷語法的程式敘述區塊。在這個程式敘述區塊中，可以使用`case`關鍵字來設定當表達式為哪些值的時候，要直接跳到程式區塊的哪個位置執行程式敘述。為避免執行到不該執行的程式敘述，可以使用`break`敘述來直接跳出switch判斷語法的程式敘述區塊。`default`關鍵字則可以用來設定當表達式的值都不符合`case`關鍵字指定的值的話，要直接跳到程式區塊的哪個位置執行程式敘述，這通常會寫在`case`關鍵字之後。

迴圈

while迴圈

`while`關鍵字所建立的迴圈可以藉由判斷表達式的值來決定要不要脫離迴圈。用法如下：

```while (條件表達式) {
條件表達式的值不為undefined、null、false、0或NaN時執行的程式敘述區塊
}```

```let number = 3;

while (number !== 0) {
console.log(number);

number = number - 1;
}

console.log("LIFTOFF!!!");```

3
2
1
LIFTOFF!!!

while迴圈內的程式敘述區塊可以使用`break``continue`敘述。前者可以立即跳出迴圈，後者可以立即進入下一次迭代。

do-while迴圈

`do``while`關鍵字所建立的迴圈可以藉由判斷表達式的值來決定要不要脫離迴圈。用法如下：

```do {
可重複執行的程式敘述區塊
} while (條件表達式)```

do-while迴圈和while迴圈的不同點在於，do-while迴圈是先執行一次迴圈內的程式敘述之後，再去用條件表達式判斷要不要再次重複執行。所以不管條件表達式是什麼，迴圈內的程式敘述都會至少被執行一次。

do-while迴圈內的程式敘述區塊也可以使用`break``continue`敘述。

for迴圈

`for`關鍵字可以用來建立計次迴圈或是走訪一個結構。用法如下：

```for ([let 變數名稱1[, 變數名稱2[, 變數名稱n]]]; [條件表達式]; [步進表達式]) {
條件表達式的值不為undefined、null、false、0或NaN時執行的程式敘述區塊
}```

```let a = [10, 20, 30, 40, 50];
let index = 0;

while (index < a.length) {
console.log(`the value is: \${a[index]}`);

index = index + 1;
}```

the value is: 10
the value is: 20
the value is: 30
the value is: 40
the value is: 50

```let a = [10, 20, 30, 40, 50];

for (let index = 0; index < a.length; index++) {
console.log(`the value is: \${a[index]}`);
}```

```let a = 1;

console.log(a);

console.log(a = a + 1);
console.log(a);

console.log(a += 1);
console.log(a);

console.log(++a);
console.log(a);

console.log(a++);
console.log(a);```

1
2
2
3
3
4
4
4
5

for迴圈內的程式敘述區塊也可以使用`break``continue`敘述。

for-in迴圈

```for (const 常數名稱 in 陣列或物件) {
可重複執行的程式敘述區塊
}```

for-in迴圈的小括號中可以使用變數或是常數，建議使用常數。

```let a = [10, 20, 30, 40, 50];

for (const index in a) {
console.log(`the value is: \${a[index]}`);
}```

for-of迴圈

```for (const 常數名稱 of 陣列或物件) {
可重複執行的程式敘述區塊
}```

for-of迴圈的小括號中可以使用變數或是常數，建議使用常數。

```let a = [10, 20, 30, 40, 50];

for (const element of a) {
console.log(`the value is: \${element}`);
}```

```let s = "Hello";

for (const element of s) {
console.log(element);
}```

H
e
l
l
o

巢狀迴圈

```let a = [10, 20, 30, 40, 50];
let b = [1, 3, 5, 7, 9];

outer: for (const index1 in a) {
for (const index2 in b) {
if (index2 === "2") {
continue outer;
}

console.log(`the value is: \${a[index1] + b[index2]}`);
}
}```

the value is: 11
the value is: 13
the value is: 21
the value is: 23
the value is: 31
the value is: 33
the value is: 41
the value is: 43
the value is: 51
the value is: 53