iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 18
2
自我挑戰組

Typescript 初心者手札系列 第 18

【Day 18】TypeScript 資料型別 - 介面(Interface)宣告與屬性

閱讀今天的文章前,先回顧一下昨天的學習,回答看看:

  • 通用型別的使用情境為何?

如果有點不清楚答案的話,可以看看 Day17 的文章喔!

介面概念

TS 的核心原則就是型別檢查,而介面(Interface)正是在 TS 中用來定義抽象物件的資料型別。介面的概念可以想像成是訂定契約,而使用此契約的物件或類別就一定會符合契約中所規定的規範,如果不符合,TS 就會報錯,但是介面只會定義描述有哪些方法(Method)和屬性(Property),無法實作。

介面宣告

在宣告一個介面時,直接使用 interface 關鍵字宣告,介面的命名一般第一個字母會是大寫,下面是一個基本的介面:

interface Person {
    //原始型別
    name: string
    age: number
    //基礎物件型別
    birthday: Date
    habits: string[]
    //函式型別
    run(meter: number): void
}

//只有單一函式,可以寫成下面的格式
interface Run{
    (meter: number):void
}

介面 Person 內部描述了屬性 name 和 age 以及方法 run 的行為,冒號前面是屬性,後面是型別,但在介面中不會撰寫執行的程式碼。有了介面後,就可以創建宣告一個變數,並要求這個變數遵守介面的規範,舉例如下:

interface Phone {
    model: string,
    price: number
}

let myPhone: Phone = {
    model: 'iphone 11',
    price: 40000
}

上面的程式碼範例中,定義了一個介面Phone,規定任何型別為 Phone 的值只能有兩個屬性:string 型別的 model 和 number 型別的 price,而後宣告了一個新的變數 myPhone,型別為 Phone,並遵守介面的規範,將model屬性賦值為字串,price屬性賦值為數字。

倘若多了一個屬性或少了一個屬性都會報錯

//多了一個屬性
let myPhone: Phone = {
    model: 'iphone 11',
    price: 40000,
    screen: '375px'
}

//Error: Type '{ model: string; price: number; screen: string; }' is not assignable to type 'Phone'.Object literal may only specify known properties, and 'screen' does not exist in type 'Phone'.

//少了一個屬性
let myPhone: Phone = {
    model: 'iphone 11',
}
//Error: Property 'price' is missing in type '{ model: string; }' but required in type 'Phone'.

// 屬性型別錯誤
let myPhone: Phone = {
    model: 'iphone 11',
    price: '40000'
}
//Error: Type 'string' is not assignable to type 'number'.

那如果屬性沒有按照順序呢?

let myPhone: Phone = {
    price: 40000,
    model: 'iphone 11',
}

答案是沒有任何影響,型別檢查器不會檢查屬性的順序,只要相對應的屬性存在且型別對就可以了。

介面只會在編譯時作用,不會編譯輸出成 JS 程式碼

可選屬性(Optional Properties)

有時候,有些屬性不一定必須,可能是某些條件下存在,這時候就可以使用之前在做型別註記的時候有使用到的可選屬性,在屬性名稱後方加上即可

interface Phone {
    model: string,
    price?: number
}

let myPhone: Phone = {
    model: 'iPhone',
}

這時候,price 屬性可有可無,就算沒有這個屬性 TS 也不會報錯。

唯讀屬性(Readonly properties)

若希望屬性只能在創建時賦值,賦值後,就再也不能改變其值了。這時候,可以在屬性名稱前面用 readonly 來指定屬性只能在創建值賦值ㄌ:

interface Phone {
   readonly model: string,
   readonly price: number
}

let myPhone: Phone = { model: 'iphone 11',price: 40000}

myPhone.model = 'ASUS' //Error: Cannot assign to 'model' because it is a read-only property.

readonly v.s. const
readonly的概念很類似常數 const,但兩者的差別就是readonly 是用在屬性上,而const 則使用在變數上。

任意屬性

某些情況下,我們可能只知道介面中部分屬性和其型別,又或者,希望能在初始化後仍然可以增加屬性,這時候就可以使用方式如下:

interface Phone {
    model: string,
    price?: number,
    [x: string]: any
}

let myPhone: Phone = {
    model: 'iphone 11',
    width: 100
}

上面的程式碼中,使用了 [x: string]定義了任意屬性的型別。要注意的是,一旦定義了任意屬性,那麼確定屬性和可選屬性都必須是它的子屬性。上面的程式碼,任意屬性的型別設定為 any ,所以除了明確訂定型別的 model 屬性外,可以任意添加其他任意型別的屬性。

但倘若任意屬性為「特定」型別,則要注意的是所有屬性(包括確定和可選屬性)都必須是「特定」型別的子屬性

interface Phone {
    model: string,
    price?: number, // Property 'price' of type 'number' is not assignable to string index type 'string'.
    [x: string]: string
}

let myPhone: Phone = {
    model: 'iphone 11',
    price: 23000,
    width: 100, //Type 'number' is not assignable to type 'string'.
}

上面的程式碼中,任意屬性為字串型別,但可選屬性 price 卻是數字型別,數字不是字串的子屬性,因此會報錯。另外,新增加的 width 屬性為數字型別,不符合任意屬性定義的字串型別,因此也會報錯。

小結

今天先初步介紹介面(Interface)宣告的寫法和各種內容屬性,暖身一下,明天會再進一步探討介面的使用情境~


上一篇
【Day 17】TypeScript 資料型別 - 通用型別(Generic Types)
下一篇
【Day 19】TypeScript 介面(Interface) v.s. 型別別名(Type Alias)
系列文
Typescript 初心者手札30

1 則留言

0
Dylan
iT邦新手 5 級 ‧ 2019-12-12 09:42:57

關於「可選屬性(Optional Properties)」

interface Phone {
    model: string,
    price?: number
}

let myPhone: Phone = {
    price: 40000,
}

應該是這樣?

interface Phone {
    model: string,
    price?: number
}

let myPhone: Phone = {
    model: 'i7',
}
Dylan iT邦新手 5 級 ‧ 2019-12-12 09:57:54 檢舉

還有這裡 @@

interface Phone {
    model: string,
    price?: number, // Property 'price' of type 'boolean | undefined' is not assignable to string index type 'string'.
    [x: string]: string
}

let myPhone: Phone = {
    model: 'iphone 11',
    price: true,
    size: '40000',
    number: 0991222222, //Type 'number' is not assignable to type 'string'.
}

price 不是 number 嗎?

Kira iT邦新手 5 級 ‧ 2019-12-30 17:20:49 檢舉

謝謝Dylan,寫得太趕案例有誤,已經修正囉

我要留言

立即登入留言