TypeScript 真香系列——接口篇

接口帶來了什麼好處

好處One —— 過去咱們寫 JavaScript

JavaScript 中定義一個函數,用來獲取一個用戶的姓名和年齡的字符串:javascript

const getUserInfo = function(user) { 
    return name: ${user.name}, age: ${user.age} 
}
複製代碼

函數調用:前端

getUserInfo({name: "koala", age: 18})
複製代碼

這對於咱們以前在寫 JavaScript 的時候,再正常不過了,可是若是這個 getUserInfo 在多人開發過程當中,若是它是個公共函數,多個開發者都會調用,若是不是每一個人點進來看函數對應註釋,可能會出現如下問題:java

// 錯誤的調用
getUserInfo() // Uncaught TypeError: Cannot read property 'name' of undefined
console.log(getUserInfo({name: "kaola"})) // name: kaola, age: undefined
getUserInfo({name: "kaola", height: 1.66}) // name: koala, age: undefined
複製代碼

JavaScript 是弱類型的語言,因此並不會對咱們傳入的代碼進行任何的檢測,有些錯你本身都說不清楚,可是就出了問題。git

TypeScript 中的 interface 能夠解決這個問題

const getUserInfo = (user: {name: string, age: number}): string => {
  return `name: ${user.name} age: ${user.age}`;
};
複製代碼

正確的調用是以下的方式:程序員

getUserInfo({name: "kaola", age: 18});
複製代碼

若是調用者出現了錯誤的調用,那麼 TypeScript 會直接給出錯誤的提示信息:github

// 錯誤的調用
getUserInfo(); // 錯誤信息:An argument for 'user' was not provided.
getUserInfo({name: "coderwhy"}); // 錯誤信息:Property 'age' is missing in type '{ name: string; }'
getUserInfo({name: "coderwhy", height: 1.88}); // 錯誤信息:類型不匹配
複製代碼

這時候你會發現這段代碼仍是有點長,代碼不便與閱讀,這時候就體現了 interface 的必要性。typescript

使用 interface 對 user 的類型進行重構。數據庫

咱們先定義一個 IUser 接口:編程

// 先定義一個接口
interface IUser {
  name: string;
  age: number;
}
複製代碼

接下來咱們看一下函數如何來寫:c#

const getUserInfo = (user: IUser): string => {
  return `name: ${user.name}, age: ${user.age}`;
};

// 正確的調用
getUserInfo({name: "koala", age: 18});
複製代碼

// 錯誤的調用和以前同樣,報錯信息也相同再也不說明。

接口中函數的定義再次改造

定義兩個接口:

type IUserInfoFunc = (user: IUser) => string;

interface IUser {
  name: string;
  age: number;
}
複製代碼

接着咱們去定義函數和調用函數便可:

const getUserInfo: IUserInfoFunc = (user) => {
  return `name: ${user.name}, age: ${user.age}`;
};
複製代碼

// 正確的調用

getUserInfo({name: "koala", age: 18});
複製代碼

// 錯誤的調用

getUserInfo();
複製代碼

好處TWO —— 過去咱們用 Node.js 寫後端接口

其實這個說明和上面相似,我再提一下,就是想證實 TypeScript 確實挺香的! 寫一個後端接口,我要特地封裝一個工具類,來檢測前端給我傳遞過來的參數,好比下圖中的validate專門用來檢驗參數的函數

可是有了 TypeScript 這個參數檢驗函數能夠省略了,咱們能夠這樣寫:

const goodParams: IGoodsBody = this.ctx.body;
複製代碼

GoodsBody就是對應參數定義的 interface,好比這個樣子

// -- 查詢列表時候使用的接口
interface IQuery {
    page: number;
    rows: number;
    disabledPage?: boolean; // 是否禁用分頁,true將會忽略`page`和`rows`參數
 }
// - 商品
export interface IGoodsQuery extends Query {
    isOnline?: string | number; // 是否出售中的商品
    goodsNo?: string; // 商品編號
    goodsName?: string; // 商品名稱
 }
複製代碼

好的,說了他的幾個好處以後,咱們開始學習interface知識吧!還有不少優勢哦!

做者簡介:koala,專一完整的 Node.js 技術棧分享,從 JavaScript 到 Node.js,再到後端數據庫,祝您成爲優秀的高級 Node.js 工程師。【程序員成長指北】做者,Github 博客開源項目 github.com/koala-codin…

接口的基礎篇

接口的定義

和 java 語言相同,TypeScript 中定義接口也是使用 interface 關鍵字來定義:

interface IQuery {
  page: number;
}
複製代碼

你會發現我都在接口的前面加了一個I,算是我的習慣吧,以前一直寫 java 代碼,另外一方面tslint要求,不然會報一個警告,是否加看我的。

接口中定義方法

看上面的接口中,咱們定義了 page 常規屬性,定義接口時候不只僅能夠有 屬性,也能夠有方法,看下面的例子:

interface IQuery {
  page: number;
  findOne(): void;
  findAll(): void;
}
複製代碼

若是咱們有一個對象是該接口類型,那麼必須包含對應的屬性和方法(無可選屬性狀況):

const q: IQuery = {
  page: 1,
  findOne() {
    console.log("findOne");
  },
  findAll() {
    console.log("findAll");
  },
};
複製代碼

接口中定義屬性

普通屬性

上面的 page 就是普通屬性,若是有一個對象是該接口類型,那麼必須包含對應的普通屬性。就不具體說了。

可選屬性

默認狀況下一個變量(對象)是對應的接口類型,那麼這個變量(對象)必須實現接口中全部的屬性和方法。

可是,開發中爲了讓接口更加的靈活,某些屬性咱們可能但願設計成可選的(想實現能夠實現,不想實現也沒有關係),這個時候就可使用可選屬性(後面詳細講解函數時,也會講到函數中有可選參數):

interface IQuery {
  page: number;
  findOne(): void;
  findAll(): void;
  isOnline?: string | number; // 是否出售中的商品
  delete?(): void
}
複製代碼

上面的代碼中,咱們增長了isOnline屬性和delete方法,這兩個都是可選的:

注意:可選屬性若是沒有賦值,那麼獲取到的值是undefined; 對於可選方法,必須先進行判斷,再調用,不然會報錯;

const q: IQuery = {
 page: 1,
 findOne() {
   console.log("findOne");
 },
 findAll() {
   console.log("findAll");
 },
};

console.log(p.isOnline); // undefined
p.delete(); // 不能調用多是「未定義」的對象。
複製代碼

正確的調用方式以下:

if (p.delete) {
  p.delete();
}
複製代碼

你們可能會問既然是可選屬性,無關緊要的,那麼爲何還要定義呢?對比起徹底不定義,定義可選屬性主要是:爲了讓接口更加的靈活,某些屬性咱們可能但願設計成可選,而且若是存在屬性,能約束類型,而這也是十分關鍵的。

只讀屬性

默認狀況下,接口中定義的屬性可讀可寫: 可是有一個關鍵字 readonly,定義的屬性值,不能夠進行修改,強制修改後報錯。

interface IQuery {
  readonly page: number;
  findOne(): void;
}
複製代碼

page屬性加了readonly關鍵字,再給它賦值會報錯。

const q: IQuery = {
  page: 1,
  findOne() {
    console.log("findOne");
  },
};
q.page = 10;// Cannot assign to 'page' because it is a read-only property.
複製代碼

接口的高級篇

函數類型接口

Interface 還能夠用來規範函數的形狀。Interface 裏面須要列出參數列表返回值類型的函數定義。寫法以下:

  • 定義了一個函數接口
  • 接口接收三個參數而且不返回任何值
  • 使用函數表達式來定義這種形狀的函數
interface Func {
    // ✔️ 定於這個函數接收兩個必選參數都是 number 類型,以及一個可選的字符串參數 desc,這個函數不返回任何值
    (x: number, y: number, desc?: string): void
}

const sum: Func = function (x, y, desc = '') {
    // const sum: Func = function (x: number, y: number, desc: string): void {
    // ts類型系統默認推論能夠沒必要書寫上述類型定義
    console.log(desc, x + y)
}

sum(32, 22)
複製代碼

注意:不過上面的接口中只有一個函數,TypeScript 會給咱們一個建議,可使用 type 來定義一個函數的類型:

type Func = (x: number, y: number, desc?: string) => void;
複製代碼

接口的實現

接口除了定義某種類型規範,也能夠和其餘編程語言同樣,讓一個類去實現某個接口,那麼這個類就必須明確去擁有這個接口中的屬性和實現其方法:

下面的代碼中會有關於修飾符的警告,暫時忽略,後面詳細講解 // 定義一個實體接口

interface Entity {
  title: string;
  log(): void;
}
複製代碼

// 實現這樣一個接口

class Post implements Entity {
  title: string;

  constructor(title: string) {
    this.title = title;
  }

  log(): void {
    console.log(this.title);
  }
}
複製代碼

有些小夥伴的疑問?我定義了一個接口,可是我在繼承這個接口的類中還要寫接口的實現方法,那我不如直接就在這個類中寫實現方法豈不是更便捷,還省去了定義接口?這是一個初學者常常會有疑惑的地方。

解答這個疑惑以前,先記住兩個字,規範!

這個規範能夠達到你一看這名字,就知道他是用來幹什麼的,而且可拓展,能夠維護。

  • 代碼設計中,接口是一種規範; 接口一般用於來定義某種規範, 相似於你必須遵照的協議,

  • 站在程序角度上說接口只規定了類裏必須提供的屬性和方法,從而分離了規範和實現,加強了系統的可拓展性和可維護性;

接口的繼承

和類同樣,接口也能繼承其餘的接口。這至關於複製接口的全部成員。接口也是用關鍵字 extends 來繼承。

interface Shape {     //定義接口Shape
    color: string;
}

interface Square extends Shape {  //繼承接口Shape
    sideLength: number;
}
複製代碼

一個 interface 能夠同時繼承多個 interface ,實現多個接口成員的合併。用逗號隔開要繼承的接口。

interface Shape {
    color: string;
}

interface PenStroke {
    penWidth: number;
}

interface Square extends Shape, PenStroke {
    sideLength: number;
}
複製代碼

  須要注意的是,儘管支持繼承多個接口,可是若是繼承的接口中,定義的同名屬性的類型不一樣的話,是不能編譯經過的。以下代碼:

interface Shape {
    color: string;
    test: number;
}

interface PenStroke extends Shape{
    penWidth: number;
    test: string;
}
複製代碼

另外關於繼承還有一點,若是如今有一個類實現了 Square 接口,那麼不只僅須要實現 Square 的方法,也須要實現 Square 繼承自的接口中的方法,實現接口使用 implements 關鍵字 。

可索引類型接口

interface和type的區別

type 能夠而 interface 不行

  • type 能夠聲明基本類型別名,聯合類型,元組等類型
// 基本類型別名
type Name = string

// 聯合類型
interface Dog {
    wong();
}
interface Cat {
    miao();
}

type Pet = Dog | Cat

// 具體定義數組每一個位置的類型
type PetList = [Dog, Pet]
複製代碼
  • type 語句中還可使用 typeof 獲取實例的 類型進行賦值
// 當你想獲取一個變量的類型時,使用 typeof

let div = document.createElement('div');
type B = typeof div
複製代碼
  • type 其餘騷操做
type StringOrNumber = string | number;  
type Text = string | { text: string };  
type NameLookup = Dictionary<string, Person>;  
type Callback<T> = (data: T) => void;  
type Pair<T> = [T, T];  
type Coordinates = Pair<number>;  
type Tree<T> = T | { left: Tree<T>, right: Tree<T> };
複製代碼

interface 能夠而 type 不行

interface 可以聲明合併

interface User {
  name: string
  age: number
}

interface User {
  sex: string
}

/* User 接口爲 { name: string age: number sex: string } */
複製代碼

另外關於type的更多內容,能夠查看文檔:TypeScript官方文檔

接口的應用場景總結

在項目中究竟怎麼用,開篇已經舉了兩個例子,在這裏再簡單寫一點,最近嘗試了一下egg+ts,學習下。在寫查詢參數檢驗的時候,或者返回固定數據的時候,都會用到接口,看一段簡單代碼,已經看完了上面的文章,本身體會下吧。

import User from '../model/user';
import Good from '../model/good';

// 定義基本查詢類型
// -- 查詢列表時候使用的接口
interface Query {
    page: number;
    rows: number;
    disabledPage?: boolean; // 是否禁用分頁,true將會忽略`page`和`rows`參數
  }

// 定義基本返回類型
type GoodResult<Entity> = {
    list: Entity[];
    total: number;
    [propName: string]: any;
};

// - 商品
export interface GoodsQuery extends Query {
    isOnline?: string | number; // 是否出售中的商品
    goodsNo?: string; // 商品編號
    goodsName?: string; // 商品名稱
}
export type GoodResult = QueryResult<Good>;

複製代碼

總結

TypeScript 仍是挺香的,預告一篇明天的發文吧,TypeScript強大的類型別名。今天就分享這麼多,若是對分享的內容感興趣,能夠關注公衆號「程序員成長指北」,加我微信(coder_qi),拉你進技術羣,長期交流學習。

參考文章

juejin.im/post/5c8fbf…

www.teakki.com/p/57dfb5a0d…

juejin.im/post/5c2723…

mp.weixin.qq.com/s/aj45tr7AZ…

cw.hubwiz.com/card/c/55b7…

Node系列原創文章

深刻理解Node.js 中的進程與線程

想學Node.js,stream先有必要搞清楚

require時,exports和module.exports的區別你真的懂嗎

源碼解讀一文完全搞懂Events模塊

Node.js 高級進階之 fs 文件模塊學習

關注我

  • 歡迎加我微信(coder_qi),拉你進技術羣,長期交流學習...
  • 歡迎關注「程序員成長指北」,一個用心幫助你成長的公衆號...
相關文章
相關標籤/搜索