本篇要介紹的是 TypeScript 型別系統中的 object。object 是屬於 Object Types 的一部分
你將會了解到 object 在 TypeScript 中的寫法、特性以及和 JavaScript object 的不同之處
官方文件上寫了這段話
In JavaScript, the fundamental way that we group and pass around data is through objects. In TypeScript, we represent those through object types.
在 JavaScript 中我們透過物件 object
去組織和傳遞資料,但在 TypeScript 中,則是透過 物件型別 object types
嘿嘿~這張圖又出現了
Object Types(物件型別)是個統稱,object
, tuple
, array
, enum
, function
都包含在內,這裡先談談原始的 object
在 TypeScript 中,物件 object
的「型別註釋」可以使用以下格式來宣告,一般是不會用 : object
因為 function, array, tuple, enum 也是屬於 object type 的一部分,直接定義 : object
無法一眼看出是什麼型別
: {
<key>: <value type>;
}
屬性 (key) 的分隔可以使用 分號(;)
、逗號(,)
,甚至省略不寫
都OK。分號或省略不寫在 TypeScript 中更為常見(interface 和 type aalias 也是)
來看看底下準備的幾個情境範例:
product
裡有 name
, price
, category
, inStock
這幾個屬性
賦值都有遵守定義的格式和型別,所以這段 ✅ Pass
// 型別註釋
let product: {
name: string;
price: number;
category: string;
inStock: boolean;
}
// 賦值
product = {
name: 'Apple',
price: 10,
category: 'Fruit',
inStock: true
}
當更改物件中的值時,完全沒有問題!當然前提是要符合最初定義的型別
product.price = 100; // ✅ Pass
product.price = '100'; // ❌ Type 'string' is not assignable to type 'number'.
product.inStock = false
賦值時故意少寫instock,結果是 ❌ 報錯啦~
就算在下面補上 instock 也是一樣
// 賦值
product = {
name: 'Apple',
price: 10,
category: 'Fruit',
}
// ❌ Property 'inStock' is missing ...
product.inStock = false;
product['inStock'] = false;
想要在 product
裡新增 id
屬性,結果會如下:
product.id = 'v231323';
// ❌ Property 'id' does not exist on type '{ name: string; price: number; category: string; inStock: boolean; }'.
product['id'] = 'v231323';
// ❌ TS7053: Element implicitly has an 'any' type because expression of type '"id"' can't be used to index type '{ name: string; price: number; category: string; inStock: boolean; }'.
// Property 'id' does not exist on type '{ name: string; price: number; category: string; inStock: boolean; }'.
不論透過 .
或 []
新增,我們都無法在 product
裡加上 id
原因是在型別推斷上,TypeScript Compiler 並沒找到 id
,代表這個物件被加了預期外的東西,在 TypeScript 中是不允許的
product.id = 'v231323';
// ❌ Property 'id' does not exist on type '{ name: string; price: number; category: string; inStock: boolean; }'.
product['id'] = 'v231323'; // ✅ Pass
這裡居然通過了😯
為什麼呢?
用 .
新增屬性
當用 product.id = 'v231323'
來新增屬性時,TypeScript Compiler 會檢查這個屬性是否已在型別定義中出現。如果沒有,就會報錯。這是為了保證型別的安全性和一致性
用 []
新增屬性
當用 product['id'] = 'v231323'
來新增屬性時,TypeScript Compiler 會認為這是一個動態的存取方式。雖然 id 不在型別定義裡,但這種存取方式允許新增屬性,不會觸發編譯錯誤
不過前提是 noImplicitAny
也要關閉就是了~
❗️雖然會通過,但非常非常非常不建議這樣做,因為它破壞了 TypeScript 靜態型別檢查的目的
在 JavaScript 中,我們可以動態的向物件新增屬性,因為
JavaScript
是動態型別語言
,物件的結構可隨時變動TypeScript
是靜態型別語言
,它的目的是在編譯時,提前捕捉型別錯誤這裡繼續沿用 product
裡加上 id
的例子
id
屬性
let product: {
id: string;
name: string;
}
索引(即鍵)
來「動態訪問」和「設置物件的屬性」,這在處理各種動態和不確定的資料結構(如來自 API 的 JSON )時特別有用{ [key: string]: string } 即索引簽名,意思是
[key: string]
表示 key
不限定個數、名稱,但型別得是字串: string
表示 value
只能是字串// 1.
let product: { [key: string]: string };
product = {
name: 'Apple',
};
// 2.
let product: {
name: string;
price: number;
category: string;
inStock: boolean;
[key: string]: any; // 允許動態屬性,實戰上不建議使用 any,後續章節會再介紹
};
product = {
name: 'Apple',
price: 10,
category: 'Fruit',
inStock: true
};
product.id = 'v231323'; // ✅ Pass
Record<Keys, Type>
是屬於 TypeScript utility types
的一種,它可以用來表示任意鍵值對的物件utility types
後面章節會介紹let product: Record<string, any> = {
name: 'Apple',
price: 10,
};
product.id = 'v231323'; // ✅ Pass
?
可選屬性
來註記非必要的屬性let product: {
name: string;
price: number;
category: string;
inStock: boolean;
id?: string; // 可選屬性
};
product = {
name: 'Apple',
price: 10,
category: 'Fruit',
inStock: true
};
product.id = 'v231323'; // ✅ Pass
P.S. 可選屬性 (?)
!== 可選串連 (?.)
物件字面量(Object Literal, 花括號 {} )
是在 JavaScript 和 TypeScript 中直接定義物件的語法
在 JavaScript 中物件字面量是開放式的(Object literals are open-ended),我們可以任意對物件屬性新增、修改
var obj = { a: 1 };
obj.b = 2; // ✅ Pass 新增屬性
如果想要在 JavaScript 中加以限制型別,可以使用 JSDoc
JSDoc 是 用在 JavaScript 中的一種標記語言,語法以 以 /** 開始,以 */ 結束
透過對函式、變數、回傳值、參數等的註釋,加以限制型別以提高維護性和安全性,還可以掃描程式碼並自動生成文檔,不過這裡就不深入談
/**
* @type {{a: number}} // obj 只包含 a 這個屬性,型別為 number
*/
var obj = { a: 1 };
obj.b = 2; // ❌ Property 'b' does not exist on type '{ a: number; }'.
[]
動態新增屬性,但非常不建議這樣做Record 型別
、索引簽名
、可選屬性
做到每天講的內容有推到 github 上喔