2019年,typescript註定成爲一個很熱門的前端技術,由於vue3.0用typescript來寫整個框架,不少框架和庫都用了typescript,deno是否是說你?還有react很火的ui框架antd-design也用typescript,趕忙抓緊typescript吧!javascript
這多是目前爲止最全最好學習的typescirpt教程,持續更新中,代碼和文檔都在github中,歡迎github star。css
爲何要學習和使用TypeScript?html
TypeScript是一種純面向對象的語言。相比JavaScript,它更能提升咱們的開發效率。並且,因爲TypeScript是JavaScript的超集,TypeScript可以和JavaScript混合使用。由於TypeScript的這個強大特性,愈來愈多的優秀框架開始嘗試使用TypeScript。前端
在編寫代碼的階段,TypeScript就可以找到大部分的錯誤,而JavaScript在這方面就沒那麼友好了。要知道,運行時錯誤越少,你的程序的bug就越少。除此以外,相比JavaScript,TypeScript的重構也更容易。vue
供參考文檔:java
官網node
1: TypeScript安裝編譯git
2: TypeScript數據類型es6
3: TypeScript函數
4: TypeScript類
5: TypeScript接口
6: TypeScript泛型
1.TypeScript 是由微軟開發的一款開源的編程語言,像後端 java、C#這樣的面嚮對象語言可讓 js 開發大型企業項目。
2.TypeScript 是 Javascript的超級,遵循最新的 ES六、Es5 規範(至關於包含了es六、es5的語法)。TypeScript擴展了JavaScript的語法。
3.最新的 Vue 、React 也能夠集成 TypeScript。
供參考文檔:
npm install -g typescript
複製代碼
typescript文件後綴名爲.ts,最後將編譯成js文件。ts是js的擴展,想要ts代碼在瀏覽器/Node環境下運行,須要把ts代碼編譯成js代碼。
npm安裝typescript後,命令行輸入tsc + 文件名就能夠把ts編譯成js。
在index.ts中:
console.log('hello typescript')
複製代碼
在命令行上,運行typescript編譯器:
tsc index.ts
複製代碼
輸出結果爲一個index.js文件,它包含了和輸入文件中相同的javascript代碼。由於typescript是javascript的超集,徹底兼容javascript。
上面的手動編譯ts很麻煩,配置開發編輯工具Visual Studio Code(Vscode),就能夠自動編譯ts。
typescript中爲了使編寫的代碼更規範,更有利於維護,增長了類型校驗(類型聲明)
在typescript中主要給咱們提供瞭如下數據類型:
相比於js的數據類型,typescript中多了元組類型、枚舉類型、任意類型、void類型和never類型。固然這些只是基礎類型,還有更多其餘類型,後面的類型推論和高級類型能夠進一步瞭解。
寫ts代碼變量能夠指定其類型,指定類型後賦值必須爲指定的類型,不然報錯。
var flag:boolean = true
flag = 123 // 錯誤,類型不一致
複製代碼
var str:string = 'this is ts';
str='haha'; //正確
// str=true; //錯誤
複製代碼
var num:number = 123;
num = 456; // 正確
// num='str'; //錯誤
複製代碼
var flag:boolean = true
flag = false // 正確
// flag=123; // 錯誤
複製代碼
undefined:
{
// 在js中,變量已聲明但未初始化爲undefined
var undefinedTest:number
// console.log(undefinedTest) // 錯誤寫法,typescript報錯,賦值了才正確
// 在typescript中,已聲明未初始化的值要直接訪問的話類型須要定義爲undefined
var undefinedTest2:undefined
console.log(undefinedTest2) // 正確寫法,輸出undefined
}
{
// 多是number類型 多是undefined
var undefinedTest3:number | undefined;
console.log(num);
}
複製代碼
null:
// null是一個空指針對象,undefined是未初始化的變量。所以,能夠把undefined看做是空的變量,而null看做是空的對象
var nullTest:null
nullTest = null
// nullTest = {} // 錯誤,定義了類型是null,值必須爲null
複製代碼
ts有兩種方式能夠定義數組。 第一種,能夠在元素類型後面接上 [],表示由此類型元素組成的一個數組:
// 第一種
var arr:number[] = [1, 2, 3]
複製代碼
第二種方式是使用數組泛型,Array<元素類型>:
// 第二種
var arr2:Array<number> = [1, 2, 3]
複製代碼
和數組相似,元素的類型不同:
let arr:[number,string] = [123,'this is ts']
複製代碼
用法:
enum 枚舉名{
標識符[=整型常數],
標識符[=整型常數],
...
標識符[=整型常數],
}
複製代碼
enum Flag {success = 1,error = 2};
let s:Flag = Flag.success // 使用枚舉類型中的值
console.log('正確狀態',s)
let f:Flag = Flag.error
console.log('錯誤狀態',f)
複製代碼
爲那些在編程階段還不清楚類型的變量指定一個類型
var number:any = 123
number = 'str'
number = true
複製代碼
typescript中的void表示沒有任何類型,通常用於定義方法的時候方法沒有返回值。
// 表示方法沒有返回任何類型
function run(): void {
console.log('run')
}
run()
複製代碼
表示的是那些永不存在的值的類型,例如異常
var a:never
// a = 123 //錯誤寫法
a = (() => {
throw new Error('錯誤');
})()
複製代碼
內容概述: 函數的定義、可選參數、默認參數、剩餘參數、函數重載、箭頭函數。
語法:
// 函數聲明
function fn(x: Type, y: Type): Type {}
// 函數表達式
var fn = (x: Type, y: Type): Type => {}
// 函數表達式:指定變量fn的類型
var fn: (x: Type, y: Type) => Type = (x, y) => {}
複製代碼
// 函數聲明法
function run(x: number, y: number): number {
return x + y;
}
// 函數表達式法
var run2 = (x: number, y: number): string => {
return 'run2'
}
run(1, 2);
run2(1, 2);
複製代碼
這段代碼中,函數run和run2指定了參數類型,調用時傳入參數類型必須保持一致。
var run3: (x: number, y: number) => string = function(x: number, y: number): string{
return 'run3';
}
run3(1, 2);
複製代碼
當給變量run3指定類型的時候,應該是函數的參數和返回值的約束類型。若是用後面學到的ts類型推論,能夠簡寫爲:
var run4: (x: number, y: number) => string = function(x, y){ // 類型推論能夠肯定函數的參數和返回值類型,也就能夠省略類型指定
return 'run4';
}
run4(1, 2);
複製代碼
function voidFnc(): void{
console.log('沒有返回值的方法用void')
}
voidFnc();
複製代碼
function electParam(name:string, age?:number):string {
// 這裏的age可傳可不傳,age就是可選參數
if(age){
return `${name} --- ${age}`
}else{
return `${name} --- 年齡保密`
}
}
console.log('可選參數', electParam('dz'))
// 注意: 可選參數必須配置到參數的最後面
// 錯誤寫法:可選參數不在最後面
// function electParam2(name?: string, age: number): string {
// ...
// }
複製代碼
// age爲默認參數
function defaultParam(name:string, age:number = 20):String {
return `${name} --- ${age}`
}
console.log('默認參數', defaultParam('dz'))
複製代碼
// sum參數傳過來的是一個數組
function sum(...result: number[]): number {
var sum = 0;
for (var i = 0; i < result.length; i++) {
sum += result[i];
}
return sum;
}
console.log('剩餘參數', sum(1, 2, 3, 4, 5, 6));
// a=1 b=2 其餘參數爲剩餘參數
function sum2(a: number, b: number, ...result: number[]): number {
var sum = a * b;
for (var i = 0; i < result.length; i++) {
sum += result[i];
}
return sum;
}
console.log('剩餘參數2', sum2(1, 2, 3, 4, 5, 6));
複製代碼
同名函數,傳入不一樣的參數,實現不一樣的功能,這就叫做函數重載。
es5中同名函數,後面會覆蓋前面的函數,ts中則不會:
function overloadingFn(x: number, y: number): number; function overloadingFn(x: string, y: string): string; // 上面定義函數的格式,下面定義函數的具體實現 function overloadingFn(x: any, y: any): any {
return x + y;
}
overloadingFn(1, 2);
overloadingFn('a', 'b');
複製代碼
這段代碼中,同名函數overloadingFn首先定義兩個函數的格式,而後再去實現功能,原來要傳入不一樣類型參數要用多個函數實現,如今能夠用同名函數來實現,這就是函數重載。
箭頭函數和es6中同樣
setTimeout(() => {
console.log('箭頭函數')
}, 1000);
複製代碼
內容概述:類的建立、靜態方法、繼承(對象冒充繼承,原型鏈繼承,對象冒充 + 原型鏈組合繼承)
es5中的面向對象、構造函數、原型與原型鏈本質能夠看這個文檔caibaojian.com/javascript-… , 我的以爲寫得很清晰。
es5類在構造函數和原型鏈裏均可以添加屬性和方法,原型鏈上的屬性會被多個實例所共享,而構造函數則不會。
function Person() {
this.name = 'Ming'
this.run = function() {
console.log(this.name + '在運動')
}
}
Person.prototype.sex = '男' // 原型鏈上的屬性會被多個實例所共享
Person.prototype.work = function() {
console.log(this.name + '在工做')
}
var p = new Person()
p.run()
p.work()
console.log(p.name)
複製代碼
調用靜態方法不須要實例化
Person.getInfo=function(){
console.log('我是靜態方法');
}
Person.getInfo();
複製代碼
對象冒充(或者叫構造函數繼承)繼承:能夠繼承構造函數裏面的屬性和方法,可是無法繼承原型鏈上面的屬性和方法
原型繼承:能夠繼承構造函數裏面的屬性和方法,也能夠繼承原型鏈上面的屬性和方法,可是實例化子類的時候無法給父類傳參
下面是經過對象冒充 + 原型鏈組合繼承,解決了上面兩種繼承方式存在的問題
function Worker(name,age){
this.name=name; /*屬性*/
this.age=age;
this.run=function(){ /*實例方法*/
alert(this.name+'在運動');
}
}
Worker.prototype.sex="男";
Worker.prototype.work=function(){
alert(this.name+'在工做');
}
function Web(name,age){
Worker.call(this,name,age); // 對象冒充繼承,能夠繼承構造函數裏面的屬性和方法,實例化子類能夠給父類傳參
}
// Web.prototype = new Worker(); // 原型鏈繼承方法一:繼承Worker構造函數和原型上全部的方法和屬性
Web.prototype = Worker.prototype; //原型鏈繼承方法二:優化了方法一重複繼承構造函數屬性和方法的問題(本質能夠看看http://caibaojian.com/javascript-object-5.html)
var w = new Web('趙四',20);
w.run();
w.work();
複製代碼
從上面能夠看出,對象冒充繼承是在子類Web構造函數裏面經過call方法繼承父類Worker的構造函數的屬性和方法;原型鏈繼承經過子類Web的原型對象等於父類Worker的原型對象來實現繼承;最後這兩種繼承的組合方式實現了完美繼承。
內容概述: ts中類的定義、繼承、類修飾符、靜態屬性和靜態方法、多態、抽象類和抽象方法
ts中類的定義和es6類的定義同樣
class PersonDefine {
name: string // 屬性,前面省略了public關鍵詞
constructor(name:string) { //構造函數
this.name = name
}
run():string { // 原型
return `${this.name}在運動`
}
}
var define = new PersonDefine('類的定義')
alert(define.run())
複製代碼
ts中繼承比es5簡單不少,用extends super實現繼承
class WebExtend extends PersonDefine {
constructor(name:string) {
super(name) // super繼承父類的構造函數,並向父類構造函數傳參
}
work():string {
return `${this.name}在工做`
}
}
var extend = new WebExtend('繼承')
alert(extend.run())
alert(extend.work())
複製代碼
修飾符:typescript裏面定義屬性的時候給咱們提供了三種修飾符
注意:屬性若是不加修飾符,默認就是公有修飾符
// 以private爲例
class PersonPrivate{
private name:string; /*被private修飾的屬性 => 私有屬性*/
constructor(name:string){
this.name=name;
}
run():string{
return `${this.name}在運動` // 私有屬性只能在當前類裏面能夠訪問
}
}
class Web extends PersonPrivate{
constructor(name:string){
super(name)
}
work(){
// return `${this.name}在工做` // 報錯,子類不能訪問父類的私有屬性
}
}
var privateName = new PersonPrivate('private')
alert(privateName.run())
// console.log(privateName.name) // 報錯,外部不能訪問類的私有屬性
複製代碼
爲何要用靜態屬性和靜態方法?jq裏面的$.ajax就是用的靜態方法
function $(element) {
return new Base(element)
}
function Base(element) {
this.element = document.getElementById(element)
this.css = function(arr, value) {
this.element.style[arr] = value
}
}
$('box').css('color','red')
$.ajax = function() {} // 想要在$上使用方法怎麼辦,用靜態方法
複製代碼
ts中實現靜態屬性和靜態方法用static
class PersonStatic{
/*公有屬性*/
public name:string;
constructor(name:string) {
this.name=name;
}
/*實例方法(須要被實例化,因此爲實例方法)*/
run(){
return `${this.name}在運動`
}
/*靜態屬性*/
static sex = '男'
/*靜態方法,裏面無法直接調用類裏面的屬性*/
static info(){
// return 'info方法' + this.name // 靜態方法不能調用本類的方法和屬性,能夠調用靜態屬性
return 'info方法' + PersonStatic.sex
}
}
console.log('靜態方法' + PersonStatic.info())
console.log('靜態屬性' + PersonStatic.sex)
複製代碼
父類定義一個方法不去實現,讓繼承它的子類去實現,每個子類的該方法有不一樣的表現
好比定義一個父類Animal,裏面的eat方法不去實現,讓子類Dog和Cat分別實現本身的eat方法
class Animal {
name:string;
constructor(name:string) {
this.name=name;
}
eat(){ // eat方法繼承它的子類去實現
}
}
class Dog extends Animal{
constructor(name:string){
super(name)
}
eat(){
return this.name+'吃糧食'
}
}
class Cat extends Animal{
constructor(name:string){
super(name)
}
eat(){
return this.name+'吃老鼠'
}
}
複製代碼
定義:用abstract關鍵字定義抽象類和抽象方法,抽象類中的抽象方法不包含具體實現而且必須在派生類(抽象類的子類)中實現
abstract class AnimalAbst{
public name:string;
constructor(name:string){
this.name=name;
}
abstract eat():any; //抽象方法不包含具體實現而且必須在派生類中實現
run(){
console.log('其餘方法能夠不實現')
}
}
// var a = new Animal() /*錯誤的寫法,抽象類不能被實例化*/
class DogAbst extends Animal{
//抽象類的子類必須實現抽象類裏面的抽象方法
constructor(name:any){
super(name)
}
eat(){
return this.name + '吃糧食'
}
}
var d = new DogAbst('小花花');
console.log('抽象類和抽象方法',d.eat());
複製代碼
接口定義:接口是對傳入參數進行約束;或者對類裏面的屬性和方法進行聲明和約束,實現這個接口的類必須實現該接口裏面屬性和方法;typescript中的接口用interface關鍵字定義。
接口做用:接口定義了某一批類所須要遵照的規範,接口不關心這些類的內部狀態數據,也不關心這些類裏方法的實現細節,它只規定這批類裏必須提供某些方法,提供這些方法的類就能夠知足實際須要。typescrip中的接口相似於java,同時還增長了更靈活的接口類型,包括屬性、函數、可索引和類等。
內容概述:接口分類:(屬性接口、函數類型接口、可索引接口、類類型接口),接口的繼承
對傳入對象的約束(也就是對json的約束)
在瞭解接口以前,咱們來看看函數傳入obj參數
function printLabel(labelInfo: {label:string}){
return labelInfo
}
// printLabel({name:'obj'}); //錯誤的寫法
console.log(printLabel({label: 'obj'}))
複製代碼
和上面相似,由此引入屬性接口 => 對方法傳入參數進行約束
下面爲屬性接口的例子,方法printFullName對傳入參數FullName(爲對象)進行約束
interface FullName{
firstName: string; // 注意;結束
secondName: string;
age?: number // 接口的可選屬性用?
}
function printFullName(name:FullName) {
// 傳入對象必須包含firstName和secondName,可傳可不傳age
return name
}
var obj = {
firstName:'小',
secondName:'明',
age: 20
}
console.log(printFullName(obj))
複製代碼
屬性接口應用:原生js封裝ajax
interface Config{
type: string;
url: string;
data?: string;
dataType: string;
}
function ajax(config: Config) {
var xhr = new XMLHttpRequest
xhr.open(config.type, config.url, true)
xhr.send(config.data)
xhr.onreadystatechange = function() {
if(xhr.readyState == 4 && xhr.status == 200) {
if(config.dataType == 'json'){
console.log(JSON.parse(xhr.responseText))
}else{
console.log(xhr.responseText)
}
}
}
}
ajax({
type: 'get',
data: 'name=xiaoming',
url: 'http://a.itying.com/api/productlist',
dataType: 'json'
})
複製代碼
對方法傳入的參數以及返回值進行約束
interface encrypt{
(key: string, value: string): string; // 傳入的參數和返回值的類型
}
var md5:encrypt = function(key:string, value:string):string{
// encrypt對加密方法md5進行約束,同時md5方法的參數和返回值類型和encrypt要保持一致
return key + value
}
console.log(md5('name', '小明'))
複製代碼
對索引和傳入參數的約束(通常用於對數組、對象的約束)
ts中定義數組:
var arr1:number[] = [1,2]
var arr2:Array<string> = ['1', '2']
複製代碼
如今用接口來實現:
// 對數組的的約束
interface UserArr{
// 索引爲number,參數爲string
[index:number]: string
}
var userarr:UserArr = ['a', 'b']
console.log(userarr)
複製代碼
// 對象的約束
interface UserObj{
// 索引爲string,參數爲string
[index:string]: string
}
var userobj:UserObj = { name: '小明', sex: '男' }
console.log(userobj)
複製代碼
對類的約束,和抽象類抽象有點類似
interface Animal{
// 對類裏面的屬性和方法進行約束
name:string;
eat(str:string):void;
}
// 類實現接口要用implements關鍵字,必須實現接口裏面聲明的方法和屬性
class Cat implements Animal{
name:string;
constructor(name:string){
this.name = name
}
eat(food:string){
console.log(this.name + '吃' + food)
}
}
var cat = new Cat('小花')
cat.eat('老鼠')
複製代碼
和類的繼承同樣,用extends實現接口繼承
下面同時實現類的繼承和接口的繼承
interface Animal {
eat(): void;
}
// 繼承Animal接口,則實現Person接口的類必須也實現Animal接口裏面的方法
interface Person extends Animal {
work(): void;
}
class Programmer {
public name: string;
constructor(name: string) {
this.name = name;
}
coding(code: string) {
console.log(this.name + code)
}
}
// 繼承類而且實現接口
class Web extends Programmer implements Person {
constructor(name: string) {
super(name)
}
eat() {
console.log(this.name + '吃')
}
work() {
console.log(this.name + '工做');
}
}
var w = new Web('小李');
w.eat();
w.coding('寫ts代碼');
複製代碼
泛型定義:泛型定義:泛型就是解決類、接口、方法的複用性,以及對不特定數據類型的支持(類型校驗)。ts中用T表示泛型。
泛型公式: 表示泛型,調用的時候指定T的數據類型
軟件工程中,咱們不只要建立一致的定義良好的API,同時也要考慮可重用性。 組件不只可以支持當前的數據類型,同時也能支持將來的數據類型,這在建立大型系統時爲你提供了十分靈活的功能。
在像C#和Java這樣的語言中,可使用泛型來建立可重用的組件,一個組件能夠支持多種類型的數據。 這樣用戶就能夠以本身的數據類型來使用組件。
內容概述:內容概述:函數的泛型、類的泛型、泛型接口
傳入的參數類型和返回的參數類型能夠指定
咱們來看看函數用ts數據類型,想要同時返回string類型和number類型
function getData1(value:string):string{
return value;
}
function getData2(value:number):number{
return value;
}
複製代碼
這樣要寫不一樣的函數,不能按照需求返回不一樣類型數據,形成代碼冗餘 => 由此引入泛型
表示泛型,調用的時候指定T的數據類型
function dataT<T>(value:T):T{
// 傳入參數爲T 返回值爲T
return value
}
dataT<number>(1) // 調用指定泛型爲number類型,則傳入參數也必須爲number類型
dataT<string>('string')
function dataAny<T>(value:T):any{
return '傳入參數爲T,任意類型返回值';
}
dataAny<number>(123); // 參數必須是number
dataAny<string>('這是一個泛型');
複製代碼
也是用來實現類的泛型,new的時候指定T的數據類型
有個最小堆算法,須要同時支持返回數字和字符串兩種類型
使用泛型以前:只能在類的類部指定數據類型,實現需求還要寫一套string類型的類
class MinClass{
public list:number[]=[];
add(num:number){
this.list.push(num)
}
min():number{
var minNum=this.list[0];
for(var i=0;i<this.list.length;i++){
if(minNum>this.list[i]){
minNum=this.list[i];
}
}
return minNum;
}
}
var m=new MinClass();
m.add(1);
m.add(2);
alert(m.min());
複製代碼
使用泛型以後:只用一套類來實現
class MinClassT<T>{
public list:T[]=[];
add(value:T):void{
this.list.push(value);
}
min():T{
var minNum=this.list[0];
for(var i=0;i<this.list.length;i++){
if(minNum>this.list[i]){
minNum=this.list[i];
}
}
return minNum;
}
}
var m1=new MinClassT<number>(); /*實例化類 而且指定了類的T表明的類型是number*/
m.add(1);
m.add(2);
alert(m1.min())
var m2=new MinClassT<string>(); /*實例化類 而且指定了類的T表明的類型是string*/
m2.add('c');
m2.add('a');
alert(m2.min())
複製代碼
有一個函數類型接口
interface ConfigFn{
(value:string):string;
}
var setData:ConfigFn = function(value:string):string{
return value
}
setData('name');
// setData(20); // 錯誤
複製代碼
setData(20);寫法錯誤,想要傳入number類型的參數又要寫一個函數類型接口 => 用泛型接口
泛型接口有兩種寫法:
// 泛型接口定義方式一
interface ConfigFnOne{
<T>(value:T):T;
}
var setDataOne:ConfigFnOne = function<T>(value:T):T{
return value
}
// 既能夠傳入string也能夠傳入number類型參數
setDataOne<string>('name');
setDataOne<number>(20);
複製代碼
// 泛型接口定義方式二
interface ConfigFnTwo<T>{
(value:T):T;
}
function setDataTwo<T>(value:T):T{
return value
}
var setDataTwoFn:ConfigFnTwo<string> = setDataTwo
setDataTwoFn('name');
複製代碼
有的時候不必定須要強制使用類型聲明,在有些沒有明確指出類型的地方,ts類型推論會幫助提供類型。
內容概述:變量初始化類型推論、上下文類型推論。
ts會根據變量初始化的時候賦予的值進行類型推斷。
let a = '類型推論';
// a = true; // Type 'true' is not assignable to type 'string'
複製代碼
上面代碼中,a初始化沒有指定類型,ts會推論出a的類型爲string,當a = true從新賦值的時候類型不匹配會報相應錯誤,vscode編譯器會提示錯誤。
ts也會根據上下文進行類型的推斷,好比在事件函數中,函數的第一個參數會根據當前綁定的事件類型推斷處理事件對象。
document.onkeydown = function(e) {
// console.log(e.button); //<- Error Property 'button' does not exist on type 'KeyboardEvent'
};
複製代碼
這個例子會獲得一個類型錯誤,ts類型檢查器根據當前綁定的事件類onkeydown自動推導e的類型爲KeyboardEvent,vscode編譯器裏鼠標放上去就有e推導出來的類型(e:KeyboardEvent)