iT邦幫忙

2023 iThome 鐵人賽

DAY 6
0
Modern Web

TypeScript 啟動!系列 第 6

[Day 06] TypeScript 你所不知道的物件型別

  • 分享至 

  • xImage
  •  

TypeScript 的物件( Object )型別規範物件的形狀(shape)。TypeScript 無法分辨簡單物件(相是使用 { } 製作的),和較為複雜的物件(以 new Blah 創建的那種)之間的差異。

換句話說也就是結構,TypeScript 是結構定型為主的設計風格;因此我們只需在意物件是否具有特定的特性。

不要以書本封面評斷一本書,雖然它是一個很好的參考點。

物件

首先我們先簡單介紹一下物件。是由最外層的 { } 包裹起來的東西,裡面可以放幾乎所有的東西~

let a = { };
let a = {
    b: 'hi'
}
console.log(a.b); // hi

很簡單吧,接下來我們來使用 TypeScript 做描述的動作(註記)

物件 ( Object )

let a: object = {
    b: 'hi'
  }
console.log(a.b); // error TS2339: Property 'b' does not exist on type 'object'.

結果 TypeScript 會報錯,特性 b 在型別 object 上並不存在。這樣我要它幹嘛?

首先我們得先知道一件事情, Object 跟 any 真的差不多了多少,對於它描述的值,Object 並不會告訴我太多資訊,只有「它是一個 JavaScript 的物件」 或 「是不是 null」沒了@@。

let a = {
    b: 'hi'
}
console.log(a.b);
console.log(typeof a); // Object
console.log(typeof a.b); // string

但是先把註記給拿掉,TypeScript 自己推論的型別就能很正常,那我們究竟要怎麼去做註記 Object 的部分呢?

其實很簡單,還記得最早的註記的圖片嗎?

https://ithelp.ithome.com.tw/upload/images/20230921/20163107hwyms24BEV.jpg

因此最簡單的一種方法只需要這麼做就可以了~

let obj: object = {};

另一種直覺的方法只需要這麼做就可以了~

let car: { brand: string } = {
    brand: 'BMW'
}

大方向的註記規則是一樣的,只是在註記 car 物件的地方需要連屬性一起宣告,這也就是「物件字面值語法( object literal syntax )」,跟型別字面值不一樣唷!

物件字面值語法( object literal syntax ):這個東西有這種形狀。

那問題來了,我怎麼可能每次都知道一定會有什麼形狀,car 裡面元素怎麼可能只有 brand 呢?

let car: {brand: string} = {
    brand: 'BMW',
}

car = { brand: 'BMW', weight: 2300 }; // Type '{ brand: string; weight: number; }' is not assignable to type '{ brand: string; }'.Object literal may only specify known properties, and 'weight' does not exist in type '{ brand: string; }'.

上面範例就明確指出,多了一個 weight 的重量要表示該怎麼辦呢?我們可以讓有些東西是選擇性的出現。

let car: {
    brand: string,
    weight?: number,
}
car = {
    brand: 'BMW',
    weight: 2300
}

多一個「 ? 」在名稱後面就可以了,這樣他會判定為 undefined 或是 number 。

那有沒有辦法讓它有任意數量的數值特性呢?

let car: {
    brand: string,
    weight?: number,
    [key: number]: boolean,
}
car = {
    brand: 'BMW',
    10: true,
    11: false,
}

[key: number]: boolean : 代表我未來有任意數目的 number 數值且它們是 boolean 。

物件 ( Object ) 小結

根據上面的範例我們對於註記物件有一點小經驗了,接下來我稍微統整一下所有的方法~

物件字面值語法( object literal syna ):這個東西有這種形狀。

如果我知道這個物件最終會長的樣貌就用這個,像是 let car: {brand: string} 。

但是要注意的是情還有一個 「確切指定( Definite Assignment )」的問題,也就是說我們需要在使用變前之前給予他一個數值,也就是確實有被指定。例如:

let i: number;
let j = i * 5; // Variable i is used before being assigned

如果沒有確定被指定的話,很有可能運算出來就變成 NaN 了。

選擇性修飾詞(如:?)

let car: {
    brand: string,
    weight?: number,
}

透過「 ? 」選擇性修飾詞可以讓 weight 可以是 number 也可以是 undefined 。當然,「 ? 」不是唯一的修飾詞~還有其他很多種。

索引特徵式( index signature )

let car: {
    brand: string,
    weight?: number,
    [key: number]: boolean,
}

car = {
    brand: 'BMW',
    10: true,
    11: false,
}

「 [key: T]: U 」這種語法就是索引特徵式( index signature ),顧名思義藉由告知 TypeScript 對於此物件,型別為T的屬性鍵值( brand 、 weight )都必須有型別為 U 的值。

撒手不管式

let obj1: object = {};
let obj2: {} = {};
let obj3: Object = {};

有三種方式可以直接註記物件的型別,但是還記得說過 object 和 any 基本上相同嗎,也會有一種不在意有什麼欄位的時候,因此在做這種註記的時候要特別注意以上三種方式。請盡量使用第一種(obj1)。顯然在這三種方式下,object 算是比較嚴格一點點的~
https://ithelp.ithome.com.tw/upload/images/20230921/20163107rYROf86n53.png

額外補充一下(前面的區域以後再来探索吧):

型別別名

我們能使用 let 、 const 、 var 來宣告某個數值的別名( alias )。想當然我們也能宣告一個型別的別名~

type Age = number;

type People = {
    name: string,
    age: Age,
}

我們就能讓 Age 也代表 number ,在建立 People 的時候就可以讓屬性更加容易理解。但畢竟這個 Age 與 People 的型別是我們自己定義的,因此 TypeScript 永遠不會幫這個忙。

TypeScript 永遠不會對別名來進行推論!!!

上面範例敘述就會有點類似之後要學習的類別與介面~以後我們再來詳細解釋一下。

聯集與交集

聯集與交集先來複習一下,圖中左邊兩張就是交集與聯集。但是要注意的是,跟以前學的定義稍微有點不一樣。

https://ithelp.ithome.com.tw/upload/images/20230921/20163107vtgBAYm3Kb.png

圖片出處

聯集:代表可以擇一,A相同或B相同或AB同時相同。
交集:代表必須要有A元的素也必須要有B的元素

通常聯集的出現與交集的出現多很多~

有了基本概念之後,我們就可以來套用在型別別名上面。

type WarriorUser = {name: string, hp: number};
type MagicianUser = {name: string, mp: number};
type RoleA = WarriorUser | MagicianUser;
type RoleB = WarriorUser & MagicianUser;

let roleA1: RoleA = {name: 'master', hp: 1, mp: 1};
let roleA2: RoleA = {name: 'master', mp: 1};
let roleA3: RoleA = {name: 'master', hp: 1, mp: 1};

let roleB1: RoleB = {name: 'master', hp: 1, mp: 1};
let roleB2: RoleB = {name: 'master', hp: 1}; // Error 因為他沒有包含 mp 的部分

上一篇
[Day 05] TypeScript 你所不知道的基礎型別
下一篇
[Day 07] TypeScript 你所不知道的 陷阱與沒有 型別
系列文
TypeScript 啟動!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言