- TypeScript是微軟開發(fā)的,基于類的面向?qū)ο缶幊?,其文件?.ts 為后綴名;
- TypeScript是JavaScript的超集,完全兼容JavaScript代碼;
- TypeScript只存活于編譯階段,編譯為JavaScript之后,在瀏覽器/Node環(huán)境下才能運(yùn)行;
- TypeScript的安裝與編譯
npm i -g typescript tsc helloworld.ts- 默認(rèn)情況下,編譯生成的js文件輸出到當(dāng)前目錄下;
-
tsc helloworld.ts --outDir ./dist:指定編譯后的js文件輸出到dist目錄。
tsconfig.json
tsc --init //生成TS的配置文件tsconfig.json
{
"compileOnSave": true, //自動(dòng)編譯并保存
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
"outFile": "./bundle.js",
"outDir": "./dist",
"strict": true,
......
"moduleResolution": "node", //按node編譯
"removeComments": true, //生成JS代碼后,移除注釋內(nèi)容
"sourceMap": true, //配合后期調(diào)試
"emitDecoratorMetadata": true, //支持元數(shù)據(jù),裝飾器需要使用
"typeRoots": [] //為TS編譯器指定檢查類型的依據(jù)文件
},
"include": [],
"exclude": []
}
-
tsconfig.json:用于配置tsc的編譯配置選項(xiàng),如js文件的輸出目錄; - 當(dāng)
tsc不指定要編譯的ts文件時(shí),編譯器會(huì)從當(dāng)前目錄開始逐級(jí)向上查找tsconfig.json - 當(dāng)指定了編譯的ts文件時(shí),
tsconfig.json會(huì)被忽略; -
--project(-p):指定一個(gè)包含tsconfig.json的目錄來進(jìn)行編譯。
編譯選項(xiàng):compilerOptions
-
outDir:編譯生成的js文件的輸出目錄,當(dāng)前目錄(./)就是tsconfig.json的所在目錄; -
outFile:合并輸出到一個(gè)JS文件中,合并文件的順序?yàn)榧虞d和依賴順序; -
module:編譯后的js所使用的模塊化系統(tǒng),none、commonjs、es2015、amd、esnext ... -
target:指定編譯后的js對(duì)應(yīng)的ECMAScript版本,es3、es5、es6、es2015、es2016、es2017 ...
指定要編譯文件
- 執(zhí)行
tsc命令,但不指定任何ts文件時(shí),默認(rèn)會(huì)編譯當(dāng)前項(xiàng)目中的所有ts文件; -
include:指定要編譯的ts文件目錄"include": [ "./src/" ] //編譯src目錄下的ts文件- 使用
glob模式,類似于正則表達(dá)式,**/遞歸匹配任意子目錄; -
*匹配0或多個(gè)字符,?匹配一個(gè)任意字符,但都不包括目錄分隔符; -
./src/*:只編譯src目錄下的ts文件,不包括src的子目錄; -
./src/**/*:遞歸編譯src目錄下的ts文件,包括子目錄;
- 使用
-
exclude:指定不編譯的ts文件目錄,默認(rèn)已經(jīng)排除了node_modules和outDir目錄
數(shù)據(jù)類型
- let 變量: 類型
let input: HTMLInputElement = document.querySelector('uname'); let value: number = Number(input.value) + 10; let a: string; a = 1; //報(bào)錯(cuò) - TS的類型:
數(shù)字、字符串、布爾型、null、undefined、數(shù)組、元組、枚舉、void、any、Never - 基本類型與包裝類型
- 基本類型:
string、number、boolean - 包裝類型:
String、Number、Boolean - 基本類型可以直接賦值給對(duì)應(yīng)的包裝類型,但反之不行。
let s: String = 'nodejs'; - 基本類型:
- 布爾類型
var flag:boolean = true; flag = false; - 數(shù)字類型
var n:number = 12; - 字符串類型
var s:string = 'hello ts'; - 數(shù)組類型
- 基本語法:
var arr:number[] = [1, 2, 3]; //元素類型為number的數(shù)組 - 泛型方式:
var arr:Array<number> = [1, 2, 3];
- 基本語法:
- 元組類型:數(shù)組的一種,可以存儲(chǔ)多種類型的元素,但 順序不可以顛倒,長(zhǎng)度不可以違規(guī),是固定的;
let tup:[number, string] = [11, 'hello']; tup[0] = 20; tup[1] = '元組';v2.6之前,超出規(guī)定個(gè)數(shù)的元素稱作越界元素,但是只要越界元素的類型是聲明類型中的一種即可,屬于聯(lián)合類型,所以沒問題。但v2.6之后,要求元組賦值的類型和個(gè)數(shù)都必須保持一致。 - 枚舉類型
enum 枚舉名{ 標(biāo)識(shí)符[=整型常數(shù)], 標(biāo)識(shí)符[=整型常數(shù)], 標(biāo)識(shí)符[=整型常數(shù)], }- 標(biāo)識(shí)符可以加引號(hào),也可以不加,
[]表示可選參數(shù)
enum Flag { success=1, error, 'undefined'=-1 }- 使用枚舉
let f:Flag = Flag.success; console.log(f); // 1- 如果標(biāo)識(shí)符都沒有賦予整型常數(shù),則默認(rèn)為從
0開始的角標(biāo)。
- 標(biāo)識(shí)符可以加引號(hào),也可以不加,
- 任意類型
var num:any = 123; num = false; -
null和undefined,定義了變量,但并未賦值,默認(rèn)是undefinedvar num:undefined; console.log(num); // undefined, var num:null = null; console.log(num); // null - 復(fù)合類型
var num:number | undefined | null; // num 可以是數(shù)字類型,也可以是undefined/null console.log(num); // undefined num = 100; console.log(num); // 100 -
void類型:表示沒有任何類型,一般用于定義沒有返回值的函數(shù);function run():void { console.log(123) } - never類型:表示從不會(huì)出現(xiàn)的值,即聲明為never類型的變量只能被never類型所賦值;
var num:never; num = (()=>{ throw new Error('some happend'); //拋出異常,不屬于任何已知類型 })(); - 類型推導(dǎo)
- 有時(shí)候不一定強(qiáng)制使用類型聲明,TS會(huì)根據(jù)語境進(jìn)行類型推導(dǎo);
- TS的變量初始化推導(dǎo)
let a; a=1; //變量a 是number類型,不允許再賦予其他類型的值;- TS的上下文推導(dǎo)
btn.onclick = function(e) {} //e: MouseEvent btn.onkeydown = function(e) {} //e: KeyboardEvent- TS會(huì)根據(jù)當(dāng)前綁定的事件,推導(dǎo)出回調(diào)函數(shù)的第一個(gè)參數(shù)類型
MouseEvent/KeyboardEvent
函數(shù)
- 函數(shù)聲明
function fn(x: Type, y: Type): Type { ... } function fn(x: number, y: number): number { //參數(shù)為number,返回值為number return x+y; } //匿名函數(shù) let fn = function(x: number, y: number): number { return x+y; } - 函數(shù)表達(dá)式
let fn:(x:Type, y:Type) => Type = function(x:Type, y:Type){ ... } let fn: (x: number, y: number) => number = function(x: number, y: number) { return x+y; } // 函數(shù)體function的參數(shù)類型可以省略,ts會(huì)進(jìn)行類型推導(dǎo) let fn: (x: number, y: number) => number = function(x, y) { return x+y; } - 形參的對(duì)象約束
- 如果傳入的是匿名對(duì)象,其屬性必須與約定屬性一致;
function print(label:{name:string}):void { console.log('print ', label.name); } print({name:'Machel'}); //print Machel- 如果不是匿名對(duì)象,則只需要包含約定的屬性即可;
let obj = { name: 'Machel', age: 20 } print(obj); //print Machel - 可選參數(shù):與ES6保持一致,必須配置在形參的末尾,且默認(rèn)值為
undefinedfunction run(name:string, age?:number):string { return 'hello ts'; } run('Jack'); run('Jack', 20); - 默認(rèn)參數(shù):指定形參的默認(rèn)值,也必須配置在形參的末尾
function run(name:string, age:number=20):string { return 'hello ts'; }- 如果手動(dòng)指定了參數(shù)的默認(rèn)值,則不能再聲明為可選參數(shù)
? - 對(duì)于默認(rèn)參數(shù),可以利用TS的自動(dòng)類型推導(dǎo),從而不聲明類型。
- 如果手動(dòng)指定了參數(shù)的默認(rèn)值,則不能再聲明為可選參數(shù)
- 剩余參數(shù):三點(diǎn)運(yùn)算符
function sum(name:string, ...rest:number[]):number { //name是一個(gè)必傳參數(shù)(字符串類型),其余所有參數(shù)(number類型)都被封裝在 rest 數(shù)組中 let sum:number = 0; for(let i=0; i<rest.length; i++) { sum += rest[i]; } return sum; } sum('Machel', 1, 2, 3); sum('Machel', 1, 2, 3, 4, 5); - 函數(shù)重載:TS需要兼容ES5和ES6,所以TS的函數(shù)重載與Java的函數(shù)重載略有不同;
- 參數(shù)個(gè)數(shù)相同
function run(name:string):string; function run(age:number):string; function run(sn:any):any { if(typeof sn === 'string') { // string return 'name is ' + sn; } else { // number return 'age is ' + sn; } } run('Jackson'); // name is Jackson run(12); // age is 12 run(true); // 編譯報(bào)錯(cuò)- 參數(shù)個(gè)數(shù)不同:借助可選參數(shù)
function run(name:string):string; function run(name:string, age:number):string; function run(name:any, age?:any):any { if(age) { // age 存在 return `name: ${name}, age: ${age}`; } else { // age 不存在 return `name is ${name}`; } } - 函數(shù)的
this- ts的函數(shù)中,
this默認(rèn)指向any,ts不能對(duì)any類型提示任何屬性和方法; - 在
tsconfig.json中,取消this默認(rèn)指向any的設(shè)置:
"compilerOptions": { "noImplicitThis": true }- 對(duì)于某些情況,如DOM事件,回調(diào)函數(shù)的
this默認(rèn)指向DOM對(duì)象,TS自動(dòng)推導(dǎo)。
- ts的函數(shù)中,
類
ES5定義類
- 構(gòu)造函數(shù)
function Persion() { this.name = 'Machel'; this.age = 20; this.show = function() { console.log(this.name, this.age); } } var p = new Persion(); p.show(); // Machel 20 - 在原型鏈上擴(kuò)展屬性和方法
Persion.prototype.sex = 'male'; Persion.prototype.work = function() { console.log(this.name + 'is work!'); } p.work(); - 原型鏈上的屬性和方法會(huì)被多個(gè)實(shí)例共享,而構(gòu)造函數(shù)中的屬性和方法只是拷貝一份給每個(gè)實(shí)例;
- 靜態(tài)方法
Persion.run = function() { console.log('run'); } Persion.run(); - 繼承:原型鏈、對(duì)象冒充,以及兩種模式的組合
- 對(duì)象冒充
function Web() { Persion.call(this); // Web繼承Persion } var w = new Web(); w.show(); // Machel 20 w.work(); // 報(bào)錯(cuò):對(duì)象冒充不能繼承原型鏈上的屬性和方法- 原型鏈繼承
function Web() { } Web.prototype = new Persion(); w.show(); // Machel 20 //但是這種方式無法給父類傳參 function Persion(name, age) { this.name = name; this.age = age; this.show = function() { console.log(this.name, this.age); } } Web.prototype = new Persion(); var w = new Web('Machel', 22); w.show(); //undefined undefined,接收不到參數(shù)!- 組合模式
function Web(name, age) { Persion.call(this, name, age); } Web.prototype = new Persion(); 或 Web.prototype = Persion.prototype; var w = new Web('Machel', 22); w.show(); // Machel 20 w.work(); // Machel is work! - 需要百度,詳細(xì)看看ES5中的類
TS的類
- TS的類與ES2015(es6)中的
class類似,同時(shí)新增了一些實(shí)用特性; - 類的定義
class Person { name:string; //屬性,默認(rèn)訪問修飾符為public,默認(rèn)值為undefined constructor(name:string) { this.name = name; } getName():string { return this.name; } run():void { console.log(this.name + ' 在 Person'); } } var p = new Person('Jackon'); console.log(p.getName()); // Jackon p.run(); // Jackon 在 Person - 類的繼承:
extends,單繼承class Web extends Person { constructor(name:string) { super(name); //必須先初始化父類的構(gòu)造器 } run():void { //覆寫父類的方法 console.log(this.name + ' 在 Web'); } } var w = new Web('Machel'); console.log(w.getName()); // Machel w.run(); // Machel 在 Web - 修飾符:
public、protected、private、readonly-
public:公有,在類內(nèi)部、子類、類外部(對(duì)象)都可以訪問,默認(rèn)修飾符 -
protected:保護(hù)類型,在類內(nèi)部、子類可以訪問,類外部(對(duì)象)不能訪問; -
private:公有,在類內(nèi)部可以訪問,子類、類外部(對(duì)象)不能訪問; -
readonly:只讀,對(duì)象只能獲取,不能重新賦值; - 在構(gòu)造函數(shù)的參數(shù)上使用修飾符,表示同時(shí)在類中創(chuàng)建該屬性,該屬性不能在類中預(yù)定義;
constructor(public age: number){ //為Person創(chuàng)建屬性age:public age: number; this.age = age; } -
- 存取器:
setter/getter的簡(jiǎn)寫形式private _age: number = 10; get age(): number { //訪問:p1.age; return this._age; } set age(age: number) { //訪問:p1.age = 20; this._age = age; } - 靜態(tài)屬性、方法:
static修飾,靜態(tài)方法中沒有this,所以只能訪問靜態(tài)屬性;class Person { public name:string|undefined; static age:number = 20; static print() { console.log('print ' + Person.age); } } Person.age; // 20 Person.print(); //print 20 - 多態(tài):繼承的一種表現(xiàn),父類引用指向子類對(duì)象!
var p:Person = new Web('Machel'); p.run(); // Machel 在 Web
抽象類
abstract:修飾抽象類和抽象方法
- 抽象類不能直接實(shí)例化,抽象方法不包含具體實(shí)現(xiàn);
- 抽象類中可以同時(shí)包含抽象方法和普通方法,但抽象方法只能放在抽象類中;
- 抽象類的子類如果不是抽象類,則必須實(shí)現(xiàn)父類的抽象方法!
abstract class Animal { public name:string; constructor(name:string) { this.name = name; } abstract eat():any; } class Dog extends Animal { constructor(name:string) { super(name); } eat() { console.log(this.name + ' is eat!') } } var d:Animal = new Dog('dog'); d.eat(); // dog is eat!
接口:interface
- 屬性接口,約束函數(shù)的對(duì)象形參
interface Options { width: number, height: number } function print(opts: Options){ console.log(opts.width, opts.number); } print({width:100, height:50})- TS類型檢測(cè)器只會(huì)檢查接口所定義的規(guī)則屬性是否存在,并不會(huì)檢查屬性的順序;
print({height:50, width:100})- 當(dāng)以匿名對(duì)象的方式傳入時(shí),必須嚴(yán)格遵守接口規(guī)則,不能有多余屬性;
print({ firstName: 'Jack', secondName: 'Machal', age: 1 //編譯報(bào)錯(cuò):匿名對(duì)象的形式傳入時(shí),只能包含接口中約束的屬性 });- 如果傳入的對(duì)象不是一個(gè)匿名對(duì)象,那么只需要包含接口規(guī)則的屬性即可;
var obj = { firstName: 'Jack', secondName: 'Machal', age: 1 } print(obj); //編譯通過:Jack Machal-
as斷言可以繞開TS檢測(cè)
print({ width: 100 } as Options); //編譯通過 - 可選屬性的接口
interface FullName { firstName:string; secondName?:string; //可選屬性 } print({ firstName: 'Jack' //不傳secondName屬性 }) - 函數(shù)接口
interface Func { (key:string, value:string):string; } var fn:Func = function(key:string, value:string):string { return key + value; } fn('Jackson', '123456'); //Jackson123456 - 可索引接口:數(shù)組、對(duì)象的約束,也就是一組
key-value的數(shù)據(jù),數(shù)量是不確定的,其中的key具有某種特性;- key的特性在于:只能是
string或number - 數(shù)組約束
interface UserArr { [index:number]:string //索引為number類型,值為string類型 } var arr:UserArr = ['aaa', 'bbb'];- 對(duì)象約束
interface UserObj { [index:string]:string //屬性名和屬性值都是string類型 } var obj:UserObj = {name:'Joker', age:'20'}- TS類是不允許對(duì)象自己直接擴(kuò)展屬性和方法的,但可以通過接口去擴(kuò)展!
class Person { name = 'Mack' } let p = new Person(); p.run = function(){ } //編譯報(bào)錯(cuò)! interface Person { [attr: string]: any } p.fly = function() { //編譯通過 console.log('fly: ', this.name); } p.fly(); // fly: Mack - key的特性在于:只能是
- 類類型接口:對(duì)類的約束,有點(diǎn)類似于抽象類
interface Animal { name:string; eat(foot:string):void; } class Dog implements Animal { name:string; constructor(name:string) { this.name = name; } eat() { //可以只實(shí)現(xiàn)接口要求的方法,忽略要求的參數(shù) console.log('eat...'); } } - 接口的繼承
interface Animal { eat():void; } interface Person extends Animal { work:void; } class Web implements Person { public name:string; constructor(name:string) { this.name = name; } eat() { console.log('eat...'); } work() { console.log('work...'); } } - 一個(gè)類可以同時(shí)實(shí)現(xiàn)多個(gè)接口,但只能繼承一個(gè)類
class A extends B implements C,D { }
泛型
- 泛型變量
function getData<T>(value:T):T { return value; } getData<number>(123); //number類型 getData<string>('Machel'); //string類型 function getData3<T>(value:T):void { console.log(value) } getData<number>(123); // 123- 泛型也可以有多個(gè)
function fn<T, S>(a:T, b:S): [T, S] { }- 還可以是數(shù)組形式
function fn<T>(a: T[]): T[] { } function fn<T>(a: Array<T>): Array<T> { } - 泛型類
class Min<T> { public list:T[] = []; add(value:T):void { this.list.push(value); } min():T { var m = this.list[0]; ... return m; } } var n = new Min<number>(); n.add(2); - 泛型作為一種類型
let fn: <T>(x: T, y: T) => number = function(x, y) { return Number(x) + Number(y); } - 泛型接口
- 方式一
interface IFn { <T>(x: T): T; } var fn:IFn = function<T>(x:T):T { return x; } fn<string>('Joker'); //Jokerinterface IFn<T> { (x: T, y: T): number } let fn: IFn<string> = function(x, y) { return Number(x) + Number(y); }- 方式二
interface IFn<T> { (x:T):T; } function fn<T>(x:T):T { return x; } var myGet:IFn<string> = fn; myGet('Joker'); - 泛型約束:
extends,約束泛型的類型范圍- 約束泛型為HTML節(jié)點(diǎn)對(duì)象
function fn<T extends HTMLElement> (ele: T) { }- 配合接口使用
interface Len { length: number } function fn<T extends Len> (e: T) { } fn(1); //編譯報(bào)錯(cuò):number類型沒有實(shí)現(xiàn) Len 接口,也不具備 length 屬性 fn('2'); //string類型實(shí)現(xiàn)了 Len 接口 - 類類型
- 如何讓一個(gè)外部函數(shù)成為創(chuàng)建對(duì)象的工廠
function getArray(constructor: Array) { return new constructor(); } let arr = getArray(Array); //編譯報(bào)錯(cuò)- 形參constructor表示Array類型的對(duì)象,而不是一個(gè)Array的構(gòu)造函數(shù),所以無法創(chuàng)建對(duì)象;
-
{new()}:表示構(gòu)造函數(shù)類型
function getInstance(constructor: {new()}) { return new constructor(); } let arr = getInstance(Array); //通過TS檢查,創(chuàng)建一個(gè)數(shù)組對(duì)象- 限制構(gòu)造函數(shù)的類型:
function getInstance(ct: {new(): Array<string>}) { return new ct(); //只能創(chuàng)建Array<string>類型的對(duì)象 } function getInstance<T>(ct: {new(): T}) { //泛型 return new ct(); } - 應(yīng)用:數(shù)據(jù)庫(kù)操作的封裝
class MySqlDB<T> { // add(info:T):boolean { //...... 把info中的屬性插入到對(duì)應(yīng)的表中 return true; } } class User { //數(shù)據(jù)庫(kù)表的映射對(duì)象 username:string; password:string; constructor(params:{ username:string, password:string }) { this.username = params.username; this.password = params.password; } } var u = new User({username:'Joker', password:'123456'}); var DB = new MySqlDB<User>(); DB.add(u);
模塊
- 內(nèi)部模塊稱為命名空間,外部模塊稱為模塊;
- 模塊在自身作用域中執(zhí)行,而不影響全局作用域,即模塊中的變量、函數(shù)、類等等對(duì)外部是不可見的;
- 外部要使用模塊內(nèi)的數(shù)據(jù),必須先讓模塊通過
export暴露出里面的數(shù)據(jù),外部再通過import引入這些數(shù)據(jù)即可;
(外部)模塊
- 創(chuàng)建目錄
modules,用于存放項(xiàng)目的模塊,創(chuàng)建一個(gè)模塊db.tsvar url = 'xxxxxx'; export function getData():any[] { return [ { title: '111' }, { title: '222' }, ] } export function save() { console.log('save...') } - 在其他模塊中引入
db.ts模塊中的方法import { getData } from './modules/db'; - 引入時(shí)起別名
import { getData as get } from './modules/db'; - 但是,
export、import會(huì)被編譯為exports、require(),瀏覽器默認(rèn)不支持,需要使用node命令執(zhí)行,或者借助webpack這樣的工具編譯為瀏覽器能執(zhí)行的代碼; - 暴露方式二
export { url, getData, save } - 暴露方式三:
export default-
export可以導(dǎo)出多次,而export default只能導(dǎo)出一次
export default getData;-
export default導(dǎo)出的是什么數(shù)據(jù),import導(dǎo)入的就是什么數(shù)據(jù)
import getData from './modules/db'; -
命名空間
- 在一個(gè)模塊中,為了避免各種變量命名相沖突,可以將相似功能的函數(shù)、類、接口等放到獨(dú)立的命名空間內(nèi);
namespace A { interface Animal { name:string; } } namespace B { interface Animal { name:string; } }- 命名空間內(nèi)的數(shù)據(jù)默認(rèn)是私有的,外部要使用這些數(shù)據(jù),也必須通過
export暴露出去;
namespace A { interface Animal { name:string; } export class Dog implements Animal { name:string='Joker'; eat() { console.log(this.name + ' eat'); } } } var d = new A.Dog(); d.eat(); // Joker eat - 命名空間內(nèi)的數(shù)據(jù)默認(rèn)是私有的,外部要使用這些數(shù)據(jù),也必須通過