type 与 interface 的区别
類型別名type
類型別名用來給一個(gè)類型起個(gè)新名字,使用type創(chuàng)建類型別名,類型別名不僅可以用來表示基本類型,還可以用來表示對(duì)象類型、聯(lián)合類型、元祖和交集。讓我們看一些例子:
type userName = string; type userId = string | number; // type arr = number[]; type arr = Array<number>; //對(duì)象類型 type Person = {id: userId;name: userName;age: number;gender: string;isWebDev: boolean; };type Tree<T> = { value: T }; const user: Person = {id: "901",name: "椿",age: 22,gender: "女",isWebDev: false, }const numbers: arr = [1, 8, 9];接口interface
接口是命名數(shù)據(jù)結(jié)構(gòu)(例如對(duì)象)的另一種方式;與type不同,interface僅限于描述對(duì)象類型。
接口的聲明語法也不同于類型別名的聲明語法。讓我們將上面的類型別名Person重寫為接口聲明:
interface Person {id: userId;name: userName;age: number;gender: string;isWebDev: boolean; }interface與type的相似之處
- 都可以描述Object和Function
兩者都可以用來描述對(duì)象或函數(shù),但語法不同:
type
type Point = {x: number;y: number; } type setPoint = (x: number, y: number) => void;interface
interface Point {x: number;y: number; } interface SetPoint {(x: number, y: number): void }- 二者都可以被繼承
interface和type都可以被繼承。
另一個(gè)值得注意的是,接口和類型別名并不互斥。類型別名可以繼承接口,反之亦然。只是在實(shí)現(xiàn)形式上,稍微有些差別。
interface繼承interface
interface Person {name: string } interface Student extends Person {stuNo: number }interface繼承type
type Person = {name: string; }; interface Student extends Person {stuNo: number }type繼承type
type Person = {name: string; }; type Student = Person & {stuNo: number; }type繼承interface
interface Person {name: string; } type Student = Person & {stuNo: number; }- 實(shí)現(xiàn)implements
類可以實(shí)現(xiàn)interface以及type(除聯(lián)合類型外)
上面提到了特殊情況,類無法實(shí)現(xiàn)聯(lián)合類型,是什么意思呢?
type Person = { name: string } | { setName(name: string): void };//無法對(duì)聯(lián)合類型Person進(jìn)行實(shí)現(xiàn) //A class can only implement an object type or intersection of object types with statically known members. class Student implements Person {name: '張三';setName(name: string): void {} }interface與type的區(qū)別
- 定義基本類型別名
type可以定義基本類型別名,但是interface無法定義,如:
- 聲明聯(lián)合類型
type可以聲明聯(lián)合類型,例如:
- 聲明元祖
type可以聲明元祖類型:
以上都是type能做到,而interface做不到的,接下來聊聊type做不到的。
- 聲明合并
如果你多次聲明一個(gè)同名的借口,typescript會(huì)將它們合并到一個(gè)聲明中,并將它們視為一個(gè)接口。這稱為聲明合并,例如:
這種情況下,如果是type的話,重復(fù)使用Person是會(huì)報(bào)錯(cuò)的:
type Person = { name: string }; type Person = { age: number };//Duplicate identifier 'Person'- 索引簽名問題
如果你經(jīng)常使用typescript,一定遇到過相似的錯(cuò)誤:
Type ‘xxx’ is not assignable to type ‘yyy’ Index signature is missing in type ‘xxx’.
例子:
interface propType {[key: string]: string; }type dataType = {title: string; };interface dataType1 {title: string; }const data: dataType = {title: '訂單頁面', };const data1: dataType1 = {title: '訂單頁面', };let props: propType; props = data; props = data1; //Error:類型“dataType1”不可分配給類型“propType”; 類型“dataType1”中缺少索引簽名我們發(fā)現(xiàn)dataType和dataType1對(duì)應(yīng)的類型一樣,但是interface定義的賦值失敗,是什么原因呢?剛開始百思不解,最后在stack overflow上找到了一個(gè)相似的問題:
并且很幸運(yùn)的找到了有效的答案:
Record<string,string>與{[key:string]:string}相同。只有當(dāng)該類型的所有屬性都已知并且可以對(duì)照該索引簽名進(jìn)行檢查時(shí),才允許將子集分配給該索引簽名類型。在您的例子中,從exampleType到Record<string,sting的所有內(nèi)容都是可以分配的。這只能針對(duì)對(duì)象字面量類型進(jìn)行檢查,因?yàn)橐坏┞暶髁藢?duì)象字面量類型,就無法更改它們。因此,索引簽名是已知的。
相反,在你使用interface去聲明變量時(shí),它們?cè)谀且豢填愋筒⒉皇亲罱K的類型。由于interfac可以進(jìn)行聲明合并,所以總有可能將新成員添加到同一個(gè)interface定義的類型上。
再結(jié)合👆第4點(diǎn) 聲明合并的講解, 這樣就很好理解了。就是說interface定義的類型是不確定的, 如果后面再來一個(gè):
interface propType{title:number }這樣propType類型就被改變了。
總結(jié)
官方推薦用interface,其他無法滿足需求的情況下用type。
但其實(shí),因?yàn)槁?lián)合類型和 交叉類型是很常用的,所以避免不了大量使用type的場景,一些復(fù)雜類型也需要通過組裝后形成類型別名來使用。
所以,如果想保持代碼統(tǒng)一,還是可以選擇使用type。通過上面的對(duì)比,類型別名其實(shí)可涵蓋interface的大部分場景。
對(duì)于React組件props及state,使用type,這樣能夠保證使用組件的地方不能隨意在上面添加屬性。如果有自定義需求,可通過 HOC二次封裝。
編寫三方庫時(shí)使用interface,其更加靈活自動(dòng)的類型合并可應(yīng)對(duì)未知的復(fù)雜使用場景。
總結(jié)
以上是生活随笔為你收集整理的type 与 interface 的区别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CSS的长度单位
- 下一篇: 解决:Elasticsearch fai