一讲坎、什么是接口
One of the core principles of typescript is to type check the shape the value has. It is sometimes called "duck type discrimination" or "structural subtype". In typescript, the function of an interface is to name these types and define contracts for your code or third-party code.
- 常用于對(duì)對(duì)象的形狀(Shape)進(jìn)行描述泉坐。
- 可用于對(duì)類的一部分行為進(jìn)行抽象
- 為類型命名和為你的代碼或第三方代碼定義契約。
二、對(duì)象與接口
2.1 對(duì)象接口簡(jiǎn)例
接口一般首字母大寫苍柏。tslint建議加上I前綴, 否則會(huì)發(fā)出警告,可在tslint中關(guān)閉它,在rules添加規(guī)則 ["interface-name":[true,"nerver-prefix"]
interface Person {
name: string,
age: number
}
const why: Person = {
name: 'why',
age: 18
}
首先我們定義了一個(gè)接口Person,接口約束了shape必須要有name,age兩個(gè)屬性,并且有對(duì)應(yīng)的數(shù)據(jù)類型,然后我們創(chuàng)建了 why對(duì)象面向Person
定義的變量比接口少一些屬性是不被允許的:
IDE 添加了tslint插件會(huì)發(fā)出警告,編譯和瀏覽器控制臺(tái)會(huì)報(bào)錯(cuò)
interface Person {
name: string,
age: number
}
const why: Person = {
name: 'why',
}
// Property 'age' is missing in type '{ name: string; }' but required in type 'Person'.
屬性溢出也是不允許的:
interface Person {
name: string,
age: number
}
const why: Person = {
name: 'why',
age: 18,
gender: 'female'
}
需要注意接口不能轉(zhuǎn)換為 JavaScript画切。 它只是 TypeScript 的一部分。
可在TypeScript學(xué)習(xí)樂(lè)園實(shí)時(shí)轉(zhuǎn)換TypeScript
2.2 可選屬性與只讀屬性
如果希望不要完全匹配形狀长踊,那么可以用可選屬性:?
如果希望對(duì)象中的一些字段只讀功舀,那么可以用 readonly 定義
interface Person {
name?: string,
readonly age: number,
run?(): void,
}
const why: Person = {
// name 鍵值對(duì)缺失不會(huì)報(bào)錯(cuò)
age: 18
}
why.run&&why.run() //判斷函數(shù)執(zhí)行
why.age++ //修改age報(bào)錯(cuò) Cannot assign to 'age' because it is a read-only property.
2.3 任意屬性
如果希望一個(gè)接口有任意的屬性,可以使用如下方式
(1) 可索引類型
interface Person {
name: string,
age?: number,
[attr: string]: any
}
const why: Person = {
name: 'why',
age: 18,
gender:'female'
}
需要注意身弊,定義了任意屬性辟汰,那么確定屬性和可選屬性的類型都必須是它的類型的子集,否則會(huì)出現(xiàn)unexpected error.
interface Person {
name: string,
age?: number,
[attr: string]: string // error
}
const why: Person = {
name: 'why',
age: 18,
gender:'female'
/* Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'.
Property 'age' is incompatible with index signature.
Type 'number' is not assignable to type 'string'. */
}
(2) 類型斷言
有兩種方法
interface Person {
name: string,
age?: number,
}
const why: Person = <Person>{
name: 'why',
age: 18,
gender:'female'
}
當(dāng)然要配置 tslint "no-angle-bracket-type-assertion":false否則tslint會(huì)報(bào)錯(cuò),因?yàn)樗扑]你使用第二種方式
interface Person {
name: string,
age?: number,
}
const why: Person = {
name: 'why',
age: 18,
gender:'female'
} as Person
(3) 類型兼容性
interface IUser {
name: string,
age: number,
}
type IUserFunc = (user: IUser) => string
const foo: IUserFunc = (user: IUser) => {
return user.name
}
foo({ name: 'clc', age: 18, gender: 'male' }) //error
// TS2345: Argument of type '{ name: string; age: number; gender: string; }' is not assignable to parameter of type 'IUser'.
//Object literal may only specify known properties, and 'gender' does not exist in type 'IUser'.
const bar = { name: 'clc', age: 18, gender: 'male' }
foo(bar)
// 用變量bar 接收參數(shù)再傳入正常編譯
直接傳參報(bào)錯(cuò),但用變量bar接收再傳入正常編譯
interface IUser {
name: string,
age: number,
}
const user: IUser = {} // missing the following properties from type 'IUser': name, age
const user = {} as IUser // right
2.4 聯(lián)合類型
interface UnionType {
bar: any[] | (() => void) | object
}
const foo:UnionType = {
bar:['w','h','y']
// bar 是一個(gè)any類型的數(shù)組
}
const foo:UnionType = {
bar() {}
// bar 是一個(gè)返回值為void的函數(shù)
}
const foo:UnionType = {
bar: {}
// bar 是一個(gè)對(duì)象
}
2.5 可索引類型
可索引類型內(nèi)有一個(gè)索引簽名,它表示了索引的類型,還有索引值對(duì)應(yīng)的值的類型
2.5.1 數(shù)字索引簽名
index 作為簽名占位,可為任意字符.
interface NumMap {
[index: number]: string
}
const obj: NumMap = {
0: 'foo',
1: 'bar',
'2': 'why',
'c': 'clc' //errorType is not assignable to type 'NumMap'.Object literal may only specify known properties, and ''c'' does not exist in type 'NumMap'.
}
obj[0] // foo
obj[2] // why
數(shù)字作為對(duì)象的鍵是轉(zhuǎn)換為字符形式的,key為數(shù)字字符串正常編譯,若為非數(shù)字字符串,報(bào)錯(cuò).
數(shù)組本身索引簽名即為number 類型
interface NumMap {
[index: number]: string
}
let arr: NumMap
arr = ['foo', 'bar']
2.5.2 字符索引簽名
interface StrMap {
[str: string]: string
}
let obj: StrMap = {
foo: 'foo',
bar: 'bar',
1: 'why' // 正常編譯
}
const arr: StrMap = ['foo','bar'] // error 缺少字符索引簽名
字符索引簽名類型包含一部分?jǐn)?shù)字索引簽名類型
可同時(shí)使用兩種索引類型,但是數(shù)字索引的返回值必須是字符索引返回值的子類型
interface StrNumMap {
[str: string]: any, // any
[index:number]: string
}
let obj: StrNumMap = {
foo: 'foo',
bar: 'bar',
1: 'why'
}
const arr: StrNumMap = ['foo','bar'] // 正常編譯
2.6 可同時(shí)面向多個(gè)接口
const user: IUser | IStudent = {}
三、函數(shù)與接口
3.1 TypeScript函數(shù)存在的問(wèn)題
為了防止錯(cuò)誤調(diào)用,給函數(shù)的參數(shù)和返回值定義了類型限制,但代碼長(zhǎng)不方便閱讀
const foo = (name: string, age: number): string => {
return `name : ${name}, age: ${age}`
}
3.1.1 使用接口重構(gòu)一
interface IUser {
name: string,
age: number,
}
const foo = ({name,age}:IUser): string => {
return `name : ${name}, age: ${age}`
}
還可以把參數(shù)對(duì)象替換成一個(gè)變量user
interface IUser {
name: string,
age: number,
}
const foo = (user:IUser): string => {
return `name : ${user.name}, age: ${user.age}`
}
3.1.2 使用接口重構(gòu)二
定義函數(shù)還可以寫成如下形式
const foo: (name: string, age: number) => string = (name, age) => {
return `name : ${name}, age: ${age}`
}
// 給一個(gè)變量foo定義了一個(gè)函數(shù)類型,并且把一個(gè)函數(shù)賦值給了foo
接著定義兩個(gè)接口
interface IUser {
name: string,
age: number,
}
interface IUserFunc {
(user:IUser):string
}
// warn Interface has only a call signature — use `type IUserFunc = // (user:IUser) =>string` instead.
第二個(gè)接口會(huì)報(bào)警告,tslint建議您如果一個(gè)函數(shù)接口只有一個(gè)方法,建議使用類型別名,type語(yǔ)句, 可配置["callable-types":false] 關(guān)掉它,或者聽(tīng)它的吧
type IUserFunc = (user: IUser) => string
接下來(lái)按步重構(gòu)函數(shù)
interface IUser {
name: string,
age: number,
}
type IUserFunc = (user: IUser) => string
const foo: (name: string, age: number) => string = (name, age) => {
return `name : ${name}, age: ${age}`
}
面向第一個(gè)接口
const foo: (user:IUser) => string = (user) => {
return `name : ${user.name}, age: ${user.age}`
}
next
const foo: IUserFunc = user => {
return `name : ${user.name}, age: ${user.age}`
}
函數(shù)簡(jiǎn)短了許多,并且看接口就可以知道函數(shù)參數(shù),返回值的類型限制
3.2 混合類型
interface Counter {
count: number,
():void
}
function getFunc(): Counter {
function fn(){
fn.count++
}
fn.count = 0
return fn
}
函數(shù)getFunc返回類型為混合類型,即做為函數(shù)和對(duì)象使用阱佛,并帶有額外的屬性帖汞。
四、類與接口
接口對(duì)類的一部分行為進(jìn)行抽象凑术。
4.1 類實(shí)現(xiàn)接口
實(shí)現(xiàn)(implements)是面向?qū)ο笾械囊粋€(gè)重要概念翩蘸。一個(gè)類只能繼承自另一個(gè)類,不同類之間可以有一些共有的特性麦萤,就可以把特性提取成接口(interfaces)鹿鳖,用 implements 關(guān)鍵字來(lái)實(shí)現(xiàn)扁眯。
(1)TypeScript中定義一個(gè)基本的類
class Person {
name: string
run:Function
constructor(name: string) {
this.name = name
this.run=()=>{}
}
}
class Rapper extends Person {
age: number
constructor(name: string, age: number) {
super(name)
this.age = age
}
rap(){
console.log('I can sing and dance tap');
}
}
(2) implements 關(guān)鍵字實(shí)現(xiàn)接口
interface Skr {
name: string,
age: number,
rap():void
}
class Person {
name: string
constructor(name: string) {
this.name = name
}
}
class Rapper extends Person implements Skr{
age: number
constructor(name: string, age: number) {
super(name)
this.age = age
}
rap(){
console.log('I can sing and dance tap');
}
}
雖然定義了一個(gè)接口,但是在接口類中的屬性和方法還是要寫類型限制.感覺(jué)代碼反而應(yīng)為接口冗余了.
但類的接口是一種規(guī)范,因?yàn)榻涌诜蛛x了規(guī)范和實(shí)現(xiàn),可通過(guò)接口輕易解讀類必須提供的屬性和方法,增強(qiáng)了可拓展性和可維護(hù)性.
(3) 一個(gè)類可實(shí)現(xiàn)多個(gè)接口
interface Skr {
name: string,
age: number,
rap():void
}
interface SkrSkr{
sing():void
}
class Person {
name: string
constructor(name: string) {
this.name = name
}
}
class Rapper extends Person implements Skr,SkrSkr{
age: number
constructor(name: string, age: number) {
super(name)
this.age = age
}
rap(){
console.log('I can sing and dance tap');
}
sing(){
console.log('I can sing and dance tap');
}
}
五、接口繼承
- 接口可以通過(guò)其他接口來(lái)擴(kuò)展自己翅帜。
- Typescript 允許接口繼承多個(gè)接口姻檀。
- 繼承使用關(guān)鍵字 extends。
5.1 單接口繼承
extends 關(guān)鍵字后加繼承的接口
interface Person {
name: string,
age: number
}
interface Students extends Person {
gender: string
}
const foo: Students = {
name: 'why',
age: 18,
gender: 'female'
}
5.2 多接口繼承
多接口之間逗號(hào)分隔
interface Sing {
sing(): void
}
interface Jump {
jump(): void
}
interface Rap extends Sing, Jump {
rap(): void
}
const foo: Rap = {
sing(){},
jump(){},
rap(){}
}
5.3 接口繼承類
常見(jiàn)的面向?qū)ο笳Z(yǔ)言中涝滴,接口是不能繼承類的绣版,但是在 TypeScript 中卻是可行的
用extends關(guān)鍵字繼承類
class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
run(): void { }
}
interface User extends Person {
gender: string
}
const foo: User = {
name: 'foo',
age: 18,
gender: 'male',
run():void { }
}
本質(zhì)上就是接口繼承接口
interface Person {
name: string,
age: number,
run(): void
}
interface User extends Person {
gender: string
}
const foo: User = {
name: 'foo',
age: 18,
gender: 'male',
run(): void { }
}
因?yàn)槁暶?class Person時(shí),除了創(chuàng)建一個(gè)名為 Person的類歼疮,同時(shí)也創(chuàng)建了一個(gè)名為 Person的類型(實(shí)例的類型)杂抽。
所以 Person既可以 當(dāng)做一個(gè)類來(lái)用,也可以把它實(shí)例的類型當(dāng)成接口使用
class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
run(): void { }
}
const foo: Person = {
name:'foo',
age:18,
run(){}
}
實(shí)例的類型是不包括構(gòu)造函數(shù)constructor、靜態(tài)屬性或靜態(tài)方法,因?yàn)樯傻膶?shí)例也不包含這些
class Person {
name: string
constructor(name: string) {
this.name = name
}
run(): void { }
static age: number
static jump(): void { }
}
const foo: Person = {
name: 'foo',
age: 18, // “age”不在類型“Person”中
run() { },
jump(): void { } // jump”不在類型“Person”中
}