Typescript | Vue3原始碼系列
TypeScript 是開源的,TypeScript 是 JavaScript 的型別的超集,它可以編譯成純 JavaScript。編譯出來的 JavaScript 可以執行在任何瀏覽器上。TypeScript 編譯工具可以執行在任何伺服器和任何系統上
- 問題: 什麼是超集
超集是集合論的術語
說到超集,不得不說另一個,子集,怎麼理解這兩個概念呢,舉個例子
如果一個集合A裡面的的所有元素集合B裡面都存在,那麼我們可以理解集合A是集合B的子集,反之集合B為集合A的超集
現在我們就能理解為Typescript
裡包含了Javascript
的所有特性,這也意味著我們可以將.js
字尾直接命名為.ts
檔案跑到TypeScript
Typescript 解決了什麼問題
一個事物的誕生一定會有其存在的價值
那麼Typescript
的價值是什麼呢?
回答這個問題之前,我們有必要先來了解一下Typescript
的工作理念
本質上是在JavaScript
上增加一套靜態型別系統(編譯時進行型別分析),強調靜態型別系統是為了和執行時的型別檢查機制做區分,TypeScript
的程式碼最終會被編譯為JavaScript
我們再回到問題本身,縮小一下範圍,Typescript
創造的價值大部分是在開發時體現的(編譯時),而非執行時,如
- 強大的編輯器智慧提示 (研發效率,開發體驗)
- 程式碼可讀性增強 (團隊協作,開發體驗)
- 編譯時型別檢查 (業務穩健,前端專案中Top10 的錯誤型別低階的型別錯誤佔比達到70%)
正文
本篇文章作為
Vue3
原始碼系列前置篇章之一,Typescript
的科普文,主要目的為了大家在面對Vue3
原始碼時不會顯得那麼不知所措,下來將介紹一些Typescript
的基本使用變數申明
基本型別
letisDone:boolean=false
letnum:number=1
letstr:string='vue3js.cn'
letarr:number[]=[1,2,3]
letarr2:Array<number>=[1,2,3]//泛型陣列
letobj:Object={}
letu:undefined=undefined;
letn:null=null;型別補充
- 列舉
Enum
使用列舉型別可以為一組數值賦予友好的名字
enumLogLevel{
info='info',
warn='warn',
error='error',
}- 元組
Tuple
允許陣列各元素的型別不必相同。比如,你可以定義一對值分別為 string和number型別的元組
//Declareatupletype
letx:[string,number];
//Initializeit
x=['hello',10];//OK
//Initializeitincorrectly
x=[10,'hello'];//Error- 任意值
Any
表示任意型別,通常用於不確定內容的型別,比如來自使用者輸入或第三方程式碼庫
letnotSure:any=4;
notSure="maybeastringinstead";
notSure=false;//okay,definitelyaboolean- 空值
Void
與 any 相反,通常用於函式,表示沒有返回值
functionwarnUser():void{
console.log("Thisismywarningmessage");
}- 介面
interface
型別契約,跟我們平常調服務端介面要先定義欄位一個理
如下例子 point 跟 Point 型別必須一致,多一個少一個也是不被允許的
interfacePoint{
x:number
y:number
z?:number
readonlyl:number
}
constpoint:Point={x:10,y:20,z:30,l:40}
constpoint2:Point={x:'10',y:20,z:30,l:40}//Error
constpoint3:Point={x:10,y:20,z:30}//Error
constpoint4:Point={x:10,y:20,z:30,l:40,m:50}//Error可選與只讀 ? 表示可選參, readonly 表示只讀
constpoint5:Point={x:10,y:20,l:40}//正常
point5.l=50//error函式引數型別與返回值型別
functionsum(a:number,b:number):number{
returna+b
}配合
interface
使用interfacePoint{
x:number
y:number
}
functionsum({x,y}:Point):number{
returnx+y
}
sum({x:1,y:2})//3泛型
泛型的意義在於函式的重用性,設計原則希望元件不僅能夠支援當前的資料型別,同時也能支援未來的資料型別
- 比如
根據業務最初的設計函式
identity
入參為String
functionidentity(arg:String){
returnarg
}
console.log(identity('100'))業務迭代過程引數需要支援
Number
functionidentity(arg:String){
returnarg
}
console.log(identity(100))//Argumentoftype'100'isnotassignabletoparameteroftype'String'.為什麼不用
any
呢?使用
any
會丟失掉一些資訊,我們無法確定返回值是什麼型別
泛型可以保證入參跟返回值是相同型別的,它是一種特殊的變數,只用於表示型別而不是值語法
<T>(arg:T):T
其中T
為自定義變數consthello:string="Hellovue!"
functionsay<T>(arg:T):T{
returnarg;
}
console.log(say(hello))//Hellovue!泛型約束
我們使用同樣的例子,加了一個
console
,但是很不幸運,報錯了,因為泛型無法保證每種型別都有.length
屬性consthello:string="Hellovue!"
functionsay<T>(arg:T):T{
console.log(arg.length)//Property'length'doesnotexistontype'T'.
returnarg;
}
console.log(say(hello))//Hellovue!從這裡我們也又看出來一個跟
any
不同的地方,如果我們想要在約束層面上就結束戰鬥,我們需要定義一個介面來描述約束條件interfaceLengthwise{
length:number;
}
functionsay<TextendsLengthwise>(arg:T):T{
console.log(arg.length)
returnarg;
}
console.log(say(1))//Argumentoftype'1'isnotassignabletoparameteroftype'Lengthwise'.
console.log(say({value:'hellovue!',length:10}))//{value:'hellovue!',length:10}交叉型別
交叉型別(Intersection Types),將多個型別合併為一個型別
interfacefoo{
x:number
}
interfacebar{
b:number
}
typeintersection=foo&bar
constresult:intersection={
x:10,
b:20
}
constresult1:intersection={
x:10
}//error聯合型別
交叉型別(Union Types),表示一個值可以是幾種型別之一。我們用豎線 | 分隔每個型別,所以 number | string | boolean表示一個值可以是 number, string,或 boolean
typearg=string|number|boolean
constfoo=(arg:arg):any=>{
console.log(arg)
}
foo(1)
foo('2')
foo(true)函式過載
函式過載(Function Overloading), 允許建立數項名稱相同但輸入輸出型別或個數不同的子程式,可以簡單理解為一個函式可以執行多項任務的能力
例我們有一個
add
函式,它可以接收string
型別的引數進行拼接,也可以接收number
型別的引數進行相加functionadd(arg1:string,arg2:string):string
functionadd(arg1:number,arg2:number):number
//實現
functionadd<T,U>(arg1:T,arg2:U){
//在實現上我們要注意嚴格判斷兩個引數的型別是否相等,而不能簡單的寫一個arg1+arg2
if(typeofarg1==='string'&&typeofarg2==='string'){
returnarg1+arg2
}elseif(typeofarg1==='number'&&typeofarg2==='number'){
returnarg1+arg2
}
}
add(1,2)//3
add('1','2')//'12'總結
通過本篇文章,相信大家對
Typescript
不會再感到陌生了下面我們來看看在
Vue
原始碼Typescript
是如何書寫的,這裡我們以defineComponent
函式為例,大家可以通過這個例項,再結合文章的內容,去理解,加深Typescript
的認識//overload1:directsetupfunction
exportfunctiondefineComponent<Props,RawBindings=object>(
setup:(
props:Readonly<Props>,
ctx:SetupContext
)=>RawBindings|RenderFunction
):{
new():ComponentPublicInstance<
Props,
RawBindings,
{},
{},
{},
//publicprops
VNodeProps&Props
>
}&FunctionalComponent<Props>
//defineComponent一共有四個過載,這裡省略三個
//implementation,closetono-op
exportfunctiondefineComponent(options:unknown){
returnisFunction(options)?{setup:options}:options
} - 列舉