1. 程式人生 > 其它 >05 - ts日常型別-開課吧學習中心

05 - ts日常型別-開課吧學習中心

# TS日常型別



課程內容:

- 基礎型別
- 陣列
- any/unknown/noImplicitAny
- 型別標註
- 函式
- 物件型別
- 聯合
- 別名
- 介面
- 斷言
- 字面型別(Literal Type)
- null and undefined
- 列舉型別



目標:熟悉TS的基本操作

## 基礎型別

string, number, boolean, null,undefined

## 陣列型別

Array\<T\>,T代表陣列中的元素型別。

思考:要求陣列中元素型別統一優勢是什麼?



## any/unkown/noImplictAny

```ts
let obj: any = { x: 0 };
// 後續都不會被檢查.
// `any`遮蔽了所有型別檢查,相當於你相信你對程式的理解是高於TS的
obj.foo();
obj();
obj.bar = 100;
obj = "hello";
const n: number = obj;
```

*Implict* : 隱式

*explict* : 顯式

配置項:noImplicitAny,當你不為變數宣告型別時,如果noImplicitAny=false,那麼它是any。如果noImplicitAny=true呢? ——報錯

```ts
let value: unknown;

value = true; // OK
value = 42; // OK
value = "Hello World"; // OK

let value3: boolean = value; // Error

```

思考:為什麼要提供`unknown`

## 型別標註

`:` 用於型別標註。

```ts
let myName: string = "Alice";

let myName = "Alice" // ? myName的型別
```



## 函式

```tsx

// greet : string -> number (Haskell)
function greet(name: string) : number {
console.log("Hello, " + name.toUpperCase() + "!!");
}

greet(42) // Error
let x : string = greet("omg") // Error
```



匿名函式的型別

```tsx
const names = ["Alice", "Bob", "Eve"];
// Array<string>

names.forEach(function (s) {
console.log(s.toUppercase()); // Error
});

names.forEach((s) => {
console.log(s.toUppercase()); // Error
});
```

知識點:contexture typing(根據上下文猜測匿名函式引數的型別)。例子中會報錯,應該是toUpperCase(C大寫)。



函式可選引數:

```ts
function print(arg1 : string, arg2 ? : string) {
console.log(arg1, arg2)
}

print("Hello", "World")
print("Hello")
```



## 物件型別



物件如果描述了型別也需要嚴格執行。

```ts
const pt : {
x : number,
y : number
} = {x : 100, y : 100}

pt.z = 10000 // Error
```

可選項:

```tsx
function printName(obj : {first : string, last ? string}) {

}

printName({first :'Bob'})
printName({first :'Alice', last : "Alisson"})
```

**`?` 表示式**

?代表可能是undefined,但是安全很多。

```ts
const o : {
a : string,
b ? : {
c : string
}
} = {a : "1"}

console.log(o.b?.c) // undefined

o.b?.c = "Hello" // Error
```



## 聯合

```tsx

function printId(id: number | string) {
console.log("Your ID is: " + id);
}
// OK
printId(101);
// OK
printId("202");
// Error
printId({ myID: 22342 });
```



聯合型別只能使用兩個型別的公共操作。

``` ts
function printId(id: number | string) {
console.log(id.toUpperCase());
// Property 'toUpperCase' does not exist on type 'string | number'.
}
```


![image-20210712083612040](.\assets\image-20210712083612040.png)

Typescript會針對聯合型別做排除法:

```tsx
function printID(id : number | string) {
if(typeof id === 'number') {
console.log(id)
return
}
console.log(id.toUpperCase())
}
```

這個也叫做型別窄化技術(後面我們會有單獨一節介紹)

## 類型別名



```ts
type Point = {
x: number;
y: number;
};


function printCoord(pt: Point) {
console.log("The coordinate's x value is " + pt.x);
console.log("The coordinate's y value is " + pt.y);
}

printCoord({ x: 100, y: 100 });
```

類型別名也可以使用聯合:

```tsx
type ID = number | string
```

注意,別名只是別名,例如:

```tsx
let x : ID = 100
// typeof x === 'number'
```

當然別名可以和它代表的型別一起工作(因為別名不是建立了新的型別):

```jsx
let id : ID = "abc"
id = 456 // OK

```

## 介面

```ts
interface Point {
x: number;
y: number;
}

function printCoord(pt: Point) {
console.log("The coordinate's x value is " + pt.x);
console.log("The coordinate's y value is " + pt.y);
}

printCoord({ x: 100, y: 100 });
```

![image-20210712084347246](.\assets\image-20210712084347246.png)

介面的宣告合併(Declaration Merging)

```tsx
interface Box {
height: number;
width: number;
}
interface Box {
scale: number;
}
let box: Box = { height: 5, width: 6, scale: 10 };
```

特別說明:你也可以把這種能力看做是向介面中新增成員的能力。



## 型別斷言 (assertion)

有時候Ts對型別的理解沒有你多,這個時候你就需要用型別斷言:

```tsx
const myCanvas =
// HTMLElement
document.getElementById("main_canvas") as HTMLCanvasElement;
```

通常TS會接收“說的通”的型別斷言。

比如: 父類 as 子類, 聯合 as 單個。

但是有的型別斷言TS會拒絕,比如:

```tsx
const x = 'hello' as number
```

TS會報一個這樣的錯誤:Conversion of type 'string' to type 'number' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.

當然有時候你可以用any as T來“欺騙”TS,或者說矇混過關:

```ts
const a = (expr as unknown) as T;
```



## 字面型別

對於常量,在TS中實際上是Literal Type。

比如:

```tsx
const someStr = "abc"
// someStr的型別是 "abc",它的值只能是abc

const foo = 1
// foo 的型別是1(而不是整數)。

// 當然這只是ts的理解,如果用typeof 操作符
// typeof someStr // 'string'
// typeof foo // 1

// 對於let
let foo = 1 // foo : number
```

可以用字面型別來約束一些特殊的函式,比如:

```tsx
function compare(a: string, b: string): -1 | 0 | 1 {
return a === b ? 0 : a > b ? 1 : -1;
}
```

當然下面是一個更加貼近真實場景的例子:

```tsx
interface Options {
width: number;
}
function configure(x: Options | "auto") {
// ...
}
configure({ width: 100 });
configure("auto");
configure("automatic"); // Argument of type '"automatic"' is not assignable to parameter of type 'Options | "auto"'.
```

字面型別的一個坑:

```tsx
function handleRequest(url : string, method : "GET" | "POST") {
// do...
}

const req = { url: "https://example.com", method: "GET" };
handleRequest(req.url, req.method);
// Error : Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'.

// 1
const req = { url: "https://example.com", method: "GET" as "GET" };

// 2
handleRequest(req.url, req.method as "GET");

// 3
const req = { url: "https://example.com", method: "GET" } as const

```

## null / undefined

null和undefined是Javascript的兩種基礎型別(Primitive type),它們描述的是不同的行為:

- undefined是一個沒有被分配值的變數
- null是一個被人為分配的空值

Typescript有一個配置項,叫做`strictNullChecks` ,這個配置項設定為`on` 的時候,在使用有可能是null的值前,你需要顯式的檢查。

```ts
function doSomething(x: string | null) {
if (x === null) {
// do nothing
} else {
console.log("Hello, " + x.toUpperCase());
}
}
```

另外, 可以用`!` 操作符,來斷言某個值不是空值:

```ts
function doSomething(x: string | null) {
console.log("Hello, " + x!.toUpperCase());
}
```



## 列舉型別



```ts
enum Direction {
Up = 1,
Down,
Left,
Right,
}
```

上面的含義, Down = 2, Left = 3, Right = 4

列舉型別最後會被翻譯成整數,因此列舉的很多性質和整數相似。比如Down.toString()會返回2,而不是`Down` 。正因為如此,列舉型別的效率很高。

當然如果你想用字串類的列舉(個人覺得沒有必要),就需要顯示的為每一項賦值:

```tsx
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
```

當然也可以混合,不過非但沒有意義,而且會減少程式碼的可讀性:

```ts
enum BooleanLikeHeterogeneousEnum {
No = 0,
Yes = "YES",
}
```

在執行時,Enum會被解釋成物件,Enum的每項會被解釋成常數。

下面這個例子可以很好的證明。

```tsx
enum E {
X,
Y,
Z,
}

function f(obj: { X: number }) {
return obj.X;
}

f(E)

```

可以用下面這個語法提取Enum中的字串,這個也叫Reverse Mapping。

```ts
E[E.X] // X
```