摘要:函數(shù)重載這個概念是在一些強類型語言中才有的,依托于,這也算是一門強類型語言了,所以就會有需要用到這種聲明的地方。
使用TypeScript已經(jīng)有了一段時間,這的確是一個好東西,雖說在使用的過程中也發(fā)現(xiàn)了一些bug,不過都是些小問題,所以整體體驗還是很不錯的。
TypeScript之所以叫Type,和它的強類型是分不開的,這也是區(qū)別于JavaScript最關(guān)鍵的一點,類型的聲明可以直接寫在代碼中,也可以多帶帶寫一個用來表示類型的描述文件*.d.ts。
常用方式首先在d.ts中是不會存在有一些簡單的基本類型定義的(因為這些都是寫在表達式、變量后邊的,在這里定義沒有任何意義),聲明文件中定義的往往都是一些復(fù)雜結(jié)構(gòu)的類型。
大部分語法都與寫在普通ts文件中的語法一致,也是export后邊跟上要導(dǎo)出的成員。
最簡單的就是使用type關(guān)鍵字來定義:
type A = { // 定義復(fù)雜結(jié)構(gòu) b: number c: string } type Func = () => number // 定義函數(shù) type Key = number | string // 多個類型組合類型
以及在TypeScript中有著很輕松的方式針對type進行復(fù)用,比如我們有一個Animal類型,以及一個Dog類型,可以使用&來進行復(fù)用。
P.S> &符號可以拼接多個
type Animal = { weight: number height: number } type Dog = Animal & { leg: number }動態(tài)的 JSON 類型指定
如果我們有一個JSON結(jié)構(gòu),而它的key是動態(tài)的,那么我們肯定不能將所有的key都寫在代碼中,我們只需要簡單的指定一個通配符即可:
type info = { [k: string]: string | number // 可以指定多個類型 } const infos: info = { a: 1, b: "2", c: true, // error 類型不匹配 }
以及在新的版本中更推薦使用內(nèi)置函數(shù)Record來實現(xiàn):
const infos: Record獲取變量的類型= { a: 1, b: "2", c: true, // error }
假如我們有一個JSON對象,里邊包含了name、age兩個屬性,我們可以通過一些TypeScript內(nèi)置的工具函數(shù)來實現(xiàn)一些有意思的事情。
通過keyof與typeof組合可以得到我們想要的結(jié)果:
const obj = { name: "Niko", age: 18 } // 如果是這樣的取值,只能寫在代碼中,不能寫在 d.ts 文件中,因為聲明文件里邊不能存在實際有效的代碼 type keys = keyof typeof obj let a: keys = "name" // pass let b: keys = "age" // pass let c: keys = "test" // error
而如果我們想要將一個類型不統(tǒng)一的JSON修改為統(tǒng)一類型的JSON也可以使用這種方式:
const obj = { name: "Niko", age: 18, birthday: new Date() } const infos: Record獲取函數(shù)的返回值類型= { name: "", age: "", birthday: 123, // 出錯,提示類型不匹配 test: "", // 提示不是`info`的已知類型 }
又比如說我們有一個函數(shù),函數(shù)會返回一個JSON,而我們需要這個JSON來作為類型。
那么可以通過ReturnType<>來實現(xiàn):
function func () { return { name: "Niko", age: 18 } } type results = ReturnType在代碼中聲明函數(shù)和class類型// 或者也可以拼接 keyof 獲取所有的 key type resultKeys = keyof ReturnType // 亦或者可以放在`Object`中作為動態(tài)的`key`存在 type infoJson = Record , string>
因為我們知道函數(shù)和class在創(chuàng)建的時候是都有實際的代碼的(函數(shù)體、構(gòu)造函數(shù))。
但是我們是寫在d.ts聲明文件中的,這只是一個針對類型的約束,所以肯定是不會存在真實的代碼的,但是如果在普通的ts文件中這么寫會出錯的,所以針對這類情況,我們需要使用declare關(guān)鍵字,表示我們這里就是用來定義一個類型的,而非是一個對象、函數(shù):
class Personal { name: string // ^ 出錯了,提示`name`必須顯式的進行初始化 } function getName (personal: Personal): name // ^ 出錯了,提示函數(shù)缺失實現(xiàn)
以下為正確的使用方式:
-declare class Personal { +declare class Personal { name: string } -function getName (personal: Personal): name +declare function getName (personal: Personal): name
當(dāng)然了,一般情況下是不建議這么定義class的,應(yīng)該使用interface來代替它,這樣的class應(yīng)該僅存在于針對非TS模塊的描述,如果是自己開發(fā)的模塊,那么本身結(jié)構(gòu)就具有聲明類型的特性。
函數(shù)重載這個概念是在一些強類型語言中才有的,依托于TypeScript,這也算是一門強類型語言了,所以就會有需要用到這種聲明的地方。
例如我們有一個add函數(shù),它可以接收string類型的參數(shù)進行拼接,也可以接收number類型的參數(shù)進行相加。
需要注意的是,只有在做第三方插件的函數(shù)重載定義時能夠放到d.ts文件中,其他環(huán)境下建議將函數(shù)的定義與實現(xiàn)放在一起(雖說配置paths也能夠?qū)崿F(xiàn)分開處理,但是那樣就失去了對函數(shù)創(chuàng)建時的約束)
// index.ts // 上邊是聲明 function add (arg1: string, arg2: string): string function add (arg1: number, arg2: number): number // 因為我們在下邊有具體函數(shù)的實現(xiàn),所以這里并不需要添加 declare 關(guān)鍵字 // 下邊是實現(xiàn) function add (arg1: string | number, arg2: string | number) { // 在實現(xiàn)上我們要注意嚴格判斷兩個參數(shù)的類型是否相等,而不能簡單的寫一個 arg1 + arg2 if (typeof arg1 === "string" && typeof arg2 === "string") { return arg1 + arg2 } else if (typeof arg1 === "number" && typeof arg2 === "number") { return arg1 + arg2 } }
TypeScript 中的函數(shù)重載也只是多個函數(shù)的聲明,具體的邏輯還需要自己去寫,他并不會真的將你的多個重名 function 的函數(shù)體進行合并
多個函數(shù)的順序問題想象一下,如果我們有一個函數(shù),傳入Date類型的參數(shù),返回其unix時間戳,如果傳入Object,則將對象的具體類型進行toString輸出,其余情況則直接返回,這樣的一個函數(shù)應(yīng)該怎么寫?
僅做示例演示,一般正常人不會寫出這樣的函數(shù)...
function build (arg: any) { if (arg instanceof Date) { return arg.valueOf() } else if (typeof arg === "object") { return Object.prototype.toString.call(arg) } else { return arg } }
但是這樣的函數(shù)重載在聲明的順序上就很有講究了,一定要將精確性高的放在前邊:
// 這樣是一個錯誤的示例,因為無論怎樣調(diào)用,返回值都會是`any`類型 function build(arg: any): any function build(arg: Object): string function build(arg: Date): number
因為TypeScript在查找到一個函數(shù)重載的聲明以后就會停止不會繼續(xù)查找,any是一個最模糊的范圍,而Object又是包含Date的,所以我們應(yīng)該按照順序從小到大進行排列:
function build(arg: Date): number function build(arg: Object): string function build(arg: any): any // 這樣在使用的時候才能得到正確的類型提示 const res1 = build(new Date()) // number const res2 = build(() => { }) // string const res3 = build(true) // any一些不需要函數(shù)重載的場景
函數(shù)重載的意義在于能夠讓你知道傳入不同的參數(shù)得到不同的結(jié)果,如果傳入的參數(shù)不同,但是得到的結(jié)果(__類型__)卻相同,那么這里就不要使用函數(shù)重載(沒有意義)。
如果函數(shù)的返回值類型相同,那么就不需要使用函數(shù)重載
function func (a: number): number function func (a: number, b: number): number // 像這樣的是參數(shù)個數(shù)的區(qū)別,我們可以使用可選參數(shù)來代替函數(shù)重載的定義 function func (a: number, b?: number): number // 注意第二個參數(shù)在類型前邊多了一個`?` // 亦或是一些參數(shù)類型的區(qū)別導(dǎo)致的 function func (a: number): number function func (a: string): number // 這時我們應(yīng)該使用聯(lián)合類型來代替函數(shù)重載 function func (a: number | string): numberInterface
interface是在TypeScript中獨有的,在JavaScript并沒有interface一說。
因為interface只是用來規(guī)定實現(xiàn)它的class對應(yīng)的行為,沒有任何實質(zhì)的代碼,對于腳本語言來說這是一個無效的操作
在語法上與class并沒有什么太大的區(qū)別,但是在interface中只能夠進行成員屬性的聲明,例如function只能夠?qū)懢唧w接收的參數(shù)以及返回值的類型,并不能夠在interface中編寫具體的函數(shù)體,同樣的,針對成員屬性也不能夠直接在interface中進行賦值:
// 這是一個錯誤的示例 interface PersonalIntl { name: string = "Niko" sayHi (): string { return this.name } } // 在 interface 中只能存在類型聲明 interface PersonalIntl { name: string sayHi (): string }
其實在一些情況下使用interface與普通的type定義也沒有什么區(qū)別。
比如我們要導(dǎo)出一個存在name和age兩個屬性的對象:
// types/personal.d.ts export interface PersonalIntl { name: string age: number } // index.d.ts import { PersonalIntl } from "./types/personal" const personal: PersonalIntl = { name: "Niko", age: 18, }
如果將interface換成type定義也是完全沒問題的:
// types/personal.d.ts export type PersonalIntl = { name: string age: number }
這樣的定義在基于上邊的使用是完全沒有問題的,但是這樣也僅僅適用于Object字面量的聲明,沒有辦法很好的約束class模式下的使用,所以我們采用interface來約束class的實現(xiàn):
import { PersonalIntl } from "./types/personal" class Personal implements PersonalIntl { constructor(public name: string, public age: number) { } // 上邊的簡寫與下述代碼效果一致 public name: string public age: number constructor (name: string, age: number) { this.name = name this.age = age } } const personal = new Personal("niko", 18)關(guān)于函數(shù)成員聲明的一些疑惑
首先,在接口中有兩種方式可以定義一個函數(shù),一個被定義在實例上,一個被定義在原型鏈上。
兩種聲明方式如下:
interface PersonalIntl { func1 (): any // 原型鏈方法 func2: () => any // 實例屬性 }
但是我們在實現(xiàn)這兩個屬性時其實是可以互相轉(zhuǎn)換的,并沒有強要求必須使用哪種方式:
class Personal implements PersonalIntl { func1 () { console.log(this) } func2 = () => { console.log(this) } }
其實這兩者在編譯后的JavaScript代碼中是有區(qū)別的,并不清楚這是一個bug還是設(shè)計就是如此,類似這樣的結(jié)構(gòu):
var Personal = /** @class */ (function () { function Personal() { var _this = this; this.func2 = function () { console.log(_this); }; } Personal.prototype.func1 = function () { console.log(this); }; return Personal; }());
所以在使用的時候還是建議最好按照interface定義的方式來創(chuàng)建,避免一些可能存在的奇奇怪怪的問題。
接口聲明的自動合并因為interface是TypeScript特有的,所以也會有一些有意思的特性,比如相同命名的interface會被自動合并:
interface PersonalIntl { name: string } interface PersonalIntl { age: number } class Personal implements PersonalIntl { name = "Niko" age = 18 }不要在 interface 中使用函數(shù)重載
在interface中使用函數(shù)重載,你會得到一個錯誤的結(jié)果,還是拿上邊的build函數(shù)來說,如果在interface中聲明,然后在class中實現(xiàn),那么無論怎樣調(diào)用,返回值的類型都會認為是any。
所以正確的做法是在class中聲明重載,在class中實現(xiàn),interface中最多只定義一個any,而非三個重載。
class Util implements UtilIntl { build(arg: Date): number build(arg: Object): string build(arg: any): any build(arg: any) { if (arg instanceof Date) { return arg.valueOf() } else if (typeof arg === "object") { return Object.prototype.toString.call(arg) } else { return arg } } }小結(jié)
有關(guān)TypeScript聲明類型聲明相關(guān)的目前就總結(jié)了這些比較常用的,歡迎小伙伴們進行補充。
在之前的版本中有存在module和namespace的定義,但是目前來看,好像更推薦使用 ES-Modules 版本的 import/export來實現(xiàn)類似的功能,而非自定義的語法,所以就略過了這兩個關(guān)鍵字相關(guān)的描述
官方文檔中有針對如何編寫聲明文件的模版,可以參考:傳送陣
參考資料keyof
Record
ReturnType 及其他的內(nèi)置函數(shù)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/98380.html
摘要:遷移至指南為什么要遷移至本身是動態(tài)弱類型的語言,這樣的特點導(dǎo)致了代碼中充斥著很多的報錯,給開發(fā)調(diào)試和線上代碼穩(wěn)定都帶來了不小的負面影響。可行性因為是的超集,不會阻止的運行,即使存在類型錯誤也不例外,這能讓你的逐步遷移至。 Vue2.5+遷移至Typescript指南 為什么要遷移至Typescript Javascript本身是動態(tài)弱類型的語言,這樣的特點導(dǎo)致了Javascript代碼...
摘要:遷移至指南為什么要遷移至本身是動態(tài)弱類型的語言,這樣的特點導(dǎo)致了代碼中充斥著很多的報錯,給開發(fā)調(diào)試和線上代碼穩(wěn)定都帶來了不小的負面影響??尚行砸驗槭堑某?,不會阻止的運行,即使存在類型錯誤也不例外,這能讓你的逐步遷移至。 Vue2.5+遷移至Typescript指南 為什么要遷移至Typescript Javascript本身是動態(tài)弱類型的語言,這樣的特點導(dǎo)致了Javascript代...
摘要:單元測試一個合格的庫應(yīng)該包含完整的單元測試。是的支持版,和是一樣的,它能夠直接運行為后綴的單元測試文件。在目錄下加入然后執(zhí)行即可看到單元測試結(jié)果。 這篇文章主要是講述如何使用 TypeScript 編寫一個完善,包含測試、文檔、持續(xù)集成的庫,涵蓋了編寫整個庫所需要的技術(shù)和工具,主要涵蓋: 項目目錄骨架 TypeScript 配置 使用 jest 單元測試 使用 vuepress 編寫...
摘要:接下來來看一段代碼示例語法與語言比較當(dāng)類型不對的時候,會提示錯誤編譯后語法聯(lián)想大致可以把它看成是加了類型系統(tǒng)的。 一篇文章學(xué)會 TypeScript (內(nèi)部分享標(biāo)題:TypeScript 基礎(chǔ)) 這篇文章是我在公司前端小組內(nèi)部的演講分享稿,目的是教會大家使用 TypeScript,這篇文章雖然標(biāo)著基礎(chǔ),但我指的基礎(chǔ)是學(xué)完后就能夠勝任 TypeScript 的開發(fā)工作。從我分享完的效果來...
閱讀 2903·2021-11-23 09:51
閱讀 3425·2021-11-22 09:34
閱讀 3325·2021-10-27 14:14
閱讀 1522·2019-08-30 15:55
閱讀 3355·2019-08-30 15:54
閱讀 1084·2019-08-30 15:52
閱讀 1902·2019-08-30 12:46
閱讀 2862·2019-08-29 16:11