typescript
介紹
TypeScript 是 JavaScript 的一個超集,主要提供了類型系統和對 ES6+ 的支持,它由 Microsoft 開發,代碼開源於 GitHub 上。
特點
-
可以在開發階段和編譯階段就發現大部分錯誤,這總比在運行時候出錯好
-
不顯式的定義類型,也能夠自動做出類型推論
-
即使 TypeScript 編譯報錯,也可以生成 JavaScript 文件
-
Google 開發的 Angular 就是使用 TypeScript 編寫的
-
TypeScript 擁抱了 ES6+ 規範
痛點
有一定的學習成本,需要理解接口(Interfaces)、泛型(Generics)、類(Classes)、枚舉類型(Enums)等前端工程師可能不是很熟悉的東西短期可能會增加一些開發成本,多寫一些類型的定義,長期維護的項目,TypeScript 能夠減少其維護成本
安裝
npm install -g typescript tsc -v 測試
編譯
tsc hello.ts
類型不匹配時,編輯報錯,但可以生成js(編輯通過),如果不希望編譯通過需要配tsconfig.json
類型
原始類型
布爾值、數值、字符串、null、undefined、Symbol
void、any、never、聯合、函數、數組類型、類 ....
內置對象類型
DOM
Document、HTMLElement、Event、NodeList ...
工具類型
Boolean、Error、Date、RegExp、Math ...
類型註解
聲明 變量:類型; // undefined || false 聲明 變量:類型=值; //嚴格要求值是定義的類型 類型權重 any > boolean number string void 聯合、函數、數組類型、類 > null undefined -> never 權重高的類型,接受低權重的類型值,反之亦然
ts變量作用域
全局作用域(項目) 默認
在一個項目目錄下,所有文件的變量都暴露在全局
模塊作用域(文件)
變量封閉在模塊內部,在內部全局使用
空類型(void)
function alertName(): void{} //一個函數不允許返回任何值
任意類型(any)
any 允許被賦值爲任意類型,任何操作都返回任意類型
類型推論
沒有明確的指定類型,依照值推斷出一個類型。
聯合類型
取值可以爲多種類型中的一種,沒列出的不可以
let myFavoriteNumber: string | number;
對象類型
依賴接口來描述,或者class描述
interface Person { name:string; //必續 readonly age:number; //只讀 address?:string; //可選 // [key:key類型]:值類型 定義對象任意屬性 [propName:string]:any //必填屬性和可選屬性,都要是任意屬性的子屬性(子數據類型) } p2={ name: '劉鐵柱', age: 16, city: '西安' }
數組類型
// 變量:類型[] // 變量:Array<類型> // let 變量:(類型名|類型名2)[] let arr:number[]=[1,2,3,4] let arr2:(number|string)[]=['aa',12] let arr4:Array<number>=[12,3]
函數類型
對一個函數有輸入和輸出,進行約束
//聲明式 //function 函數名(參數:類型):返回類型{} function sum(x: number, y: number): number {} //函數表達式 //let 變量:輸入類 => 輸出類型 = function(參數){} let show3:(a:number,b:string,c?) => number = function (a,b){ return a; } //接口定義函數類型 interface Func { (a:number,b:string):boolean } let show5:Func = function(a,b){return false}
可選參數在後
類型斷言
繞過編譯器的類型推斷,手動指定一個值的類型
//<類型>變量 //變量 as 類型 //類型斷言不做類型轉換 //自定義類型也可以用來做斷言 //權重最低 (<string>a).length //斷言字符 (a as string).length
類型聲明文件
ts 使用第三方庫時,我們需要引用它的聲明文件,declare 關鍵字來定義它的類型,幫助 TypeScript 判斷我們傳入的參數類型對不對,聲明文件xx.d.ts
declare let $:(any)=>any; //定義 //引入文件 三斜線指令 /// <reference path="./jQuery.d.ts" />
安裝第三方的庫,選擇安裝類型聲明文件,庫具備了類型
npm i jquery -S npm i @types/jquery import * as $ from 'jquery' $ 具備類型的 //注意 現在絕大部分的庫都具備了類型聲明文件,而無需單獨安裝
內置對象類型
Boolean、Error、Date、RegExp,Math ...
Document、HTMLElement、Event、NodeList ..
let b2:Boolean = new Boolean();//對類型內置 let oDiv:HTMLDivElement = document.createElement('div'); //重寫 內置對象類型 interface Math { pow(x:number,y:string):number|string } Math.pow(12,'qq')
類
定義了一件事物的抽象特點,包含它的屬性和方法,對象是類的實例,通過 new 生成,OOP特性封裝、繼承、多態,類成員(實例屬性,方法,靜態屬性、方法)
訪問控制符
控制類內部的屬性(實例,類)|方法(實例,類) 的被訪問權限
//public 公共的 誰(父子)都可以訪問(類內外) //protected 當前類內使用 + 子類類內 //private 當前類內使用 class Person { public namee:string='alex';//實例屬性 private age:number; //沒有默認值的實例屬性 protected address:string; //沒有默認值的實例屬性 address2:string; //不加的情況下 是public protected static VER:string='1.11.1' //類屬性 靜態屬性 constructor(public namee:string,public age:number){ this.namee = namee;//實例屬性第一次的修改 this.age = age||0;//實例屬性第一次的修改 this.address } private show(){} private static show2(){} }
接口
用於對象的形狀描述,函數的類型、輸入出返回值的描述,對類的行爲進行抽象
實現接口
不同類之間可以有一些共有的特性,這時候就可以把特性提取成接口(interfaces),用 implements 關鍵字來實現,對象函數類都可以實現接口
class Person implements Action1{ ... } class Person implements Action1,Action2{ ... }
泛型
在定義函數、接口或類的時候,不預先指定具體的類型,而在使用的時候再指定類型的一種特性
函數泛型
//定義 function 函數<T>(length: number, value: T): Array<T> {} //調用 函數<string>(參數) //使用的時候再指定類型 //栗子 function swap<T,U>(tuple:[T,U]):[U,T]{ return [tuple[1],tuple[0]] } let result = swap([7,'seven'])// reuslt = ['seven',7]
接口泛型
interface SearchFunc { <T>(source:string,subString:T):Array<T> } //約定函數 let mySearch:SearchFunc = function<T>(source,subString){ return [subString] } mySearch<object>('qq',{a:1,b:2}) //約定一個類 interface SearchClass<T>{ title?:string eat(source:string,subString:T):Array<T> } class Person3 implements SearchClass<boolean>{ title?: string = 'bmw' eat(source: string, subString: boolean): boolean[] { //業務邏輯 return [true] } }
類泛型
class Person<T,U>{ msg:Array<T>; msg2:[T,U] constructor(msg:Array<T>){ } show<A,V>(arr:[V,A]):void{ //.. } } new Person<string,number>(['bmw']) new Person<boolean,number>([false])
裝飾器
-
是一種特殊類型的聲明,它能夠被附加到類聲明,方法, 訪問符,屬性或參數上去裝飾他們
-
裝飾器使用 @expression這種形式
-
expression求值後必須爲一個函數
-
它會在運行時被調用,被裝飾的聲明信息做爲參數傳入
定義
// vuex-class.ts function State(type:any):any{ return function(){ console.log('裝飾器業務') } } function Mutation(type:string):any{ return function(){ console.log('裝飾器業務') } } function Component(target:any){ // do something with "target" ... console.log('裝飾器',target) } export {State,Mutation,Component}
使用
import {Component,Mutations,State} from 'vuex-class' //裝飾類 @Component class App {} Component( class App2{} ) //裝飾類成員- 方法 @Component class App{ @Mutations('ADD_ITEM') ADD_ITEM:string // this.ADD_ITEM(信息) } //裝飾類成員- 屬性 @Component class App{ @State('bNav') stateNav:string // {{this.stateNav}} }
元組(Tuple)
表示一個已知元素數量和類型的數組
let x: [string,number]; x = ['hello',10] x[1]//10 let [a,b,c] = x; // a = hello b = 10 c= undefined
枚舉(enum)
爲一組數值賦予友好的名字
enum Color {Red,Green,Blue,Yellow};//數據位 打0開始 let c: Color = Color.Blue // ouput 2 返回的是數據位 let ColorName:string = Color[2] // 'Blue' let ColorName2:string = Color[c] // 'Blue'
never
永不存在的值的類型,是任何類型的子類型(包括undefined|null)
let a:never;//never let a2: never = a; // never只接受never類型的值 let a3: never = 10; //不能有其他值 function show (arg:number):never{ //返回never的函數必須存在無法達到的終點 // return arg; // return undefined // return undefined // while(true){} throw Error('....') }
object
非原始類型
function createElement (o:object|null):object{ return o; } let o1 = createElement({prop:'title'}) // ok let o2 = createElement(null) // createElement() export {}
類型別名(type)
-
類型別名會給一個類型起個新名字
-
類型別名有時和接口很像,不可以extends和 implements
-
但是可以作用於原始值,聯合類型,元組以及其它任何你需要手寫的類型
-
不會新建一個類型
接口(intervace) vs 別名(type)
inteface | type | |
---|---|---|
約定對象類型 | √ | √ |
約定函數類型 | √ | √ |
同名合併 | √ | × |
基礎類型、聯合類型、元組、自定義類型 | × | √ |
公共的用 interface 實現,不能用 interface 實現的再用 type 實現。
栗子
type Name = string;//類型別名 type NameResolver = ()=>string;//函數類型,返回字符 function getName(n:NameResolver):Name{ if(typeof n === 'string'){ return n; }else{ return n() } } type Container<T> = {a:number,value:T,b:string} //別名 也可以含有泛型 class App { o:Container<boolean>={a:12,value:false,b:'bmw'} }
交叉類型
類型保護
字面量類型
映射類型
索引類型
TIPS
keyof
interface iPeople { name: string; age: number } type T = keyof iPeople // -> "name" | "age"
in
type Keys = "a" | "b" type Obj = { [p in Keys]: any } // -> { a: any, b: any }
typeof
一般我們都是先定義類型,再去賦值使用,但是使用 typeof
我們可以把使用順序倒過來
const options = { a: 1 } type Options = typeof options
條件類型
條件類型可以根據其他類型的特性做出類型的判斷
T extends U ? X : Y
舉例
interface Id { id: number, /* other fields */ } interface Name { name: string, /* other fields */ } declare function createLabel(id: number): Id; declare function createLabel(name: string): Name; declare function createLabel(name: string | number): Id | Name;
使用條件類型
type IdOrName<T extends number | string> = T extends number ? Id : Name; declare function createLabel<T extends number | string>(idOrName: T): T extends number ? Id : Name;
工具泛型
Partial
作用是將傳入的屬性變爲可選項.
interface iPeople { title: string; name: string; } const people: Partial<Todo> = { title: 'Delete inactive users', }; //定義的結構可以是接口iPeople的任意key
Readonly
Readonly 作用是將傳入的屬性變爲變成只讀
interface iPeople { title: string; name: string; } const people: Readonly<Todo> = { title: 'todo list', name: chenfeng; }; //title name屬性就是隻讀的了
Required
Required 的作用是將傳入的屬性變爲必選項
interface iPeople { title?: string; name?: string; } const people: Required<iPeople> = { title: 'ts' }; // Error: property 'name' missing
Exclude<T,U>
從 T
中排除那些可以賦值給 U
的類型。
Exclude
實現源碼 node_modules/typescript/lib/lib.es5.d.ts
。
type Exclude<T, U> = T extends U ? never : T;
實例:
type T = Exclude<1|2|3|4|5, 3|4> // T = 1|2|5
此時 T
類型的值只可以爲 1
、2
、 5
,當使用其他值是 TS 會進行錯誤提示。
Error:(8, 5) TS2322: Type '3' is not assignable to type '1 | 2 | 5'.
Extract<T,U>
從 T
中提取那些可以賦值給 U
的類型。
Extract實現源碼 node_modules/typescript/lib/lib.es5.d.ts
。
type Extract<T, U> = T extends U ? T : never;
實例:
type T = Extract<1|2|3|4|5, 3|4> // T = 3|4
此時T類型的值只可以爲 3
、4
,當使用其他值時 TS 會進行錯誤提示:
Error:(8, 5) TS2322: Type '5' is not assignable to type '3 | 4'.
Pick<T,K>
從 T
中取出一系列 K
的屬性。
Pick
實現源碼 node_modules/typescript/lib/lib.es5.d.ts
。
type Pick<T, K extends keyof T> = { [P in K]: T[P]; };
實例:
假如我們現在有一個類型其擁有 name
、 age
、 sex
屬性,當我們想生成一個新的類型只支持 name
、age
時可以像下面這樣:
interface Person { name: string, age: number, sex: string, } let person: Pick<Person, 'name' | 'age'> = { name: '小王', age: 21, }
Record<string,any>
Record<string,any> 用這個來聲明對象結構的類型
//用於定義一個javascript的對象,key是字符串,value是任意類型 const people:Record<string,any> = { name: 'chengfeng', age: 10 }