Typescript
基礎(chǔ)類型
// 布爾 boolean
let flag:boolean = true;
// 數(shù)字類型 number
let num:number = 100;
// 字符串類型 string
let str:string = "Hello";
// 數(shù)組類型 array
let arr:number[] = [1,2,3,4,5];
let arr:Array<number> = [1,2,3,4,5];
// 元組類型 tuple(數(shù)組的增強(qiáng)可指定多種類型)
let arr:[string,number] = [1,"a"];
// 枚舉類型 enum(用于標(biāo)識(shí)狀態(tài)和固定值)
enum Flag{
success=1,
error=-1
};
let f:Flag = Flag.success;
enum Color {red=2,blue,orange}; // 如果不定義值,則是其索引值
let c:Color = Color.blue; // 3,上一個(gè)值有定義則在此基礎(chǔ)上+1
// 任意類型 any(跟js原本的變量無區(qū)別)
let str:any = "Hello";
let num:any = 123;
// null 和 undefined
let num:number | undefined;
num = 123;
// void 類型(無任何值類型,常用于方法無返回值)
function fun():void{
}
// never 從來不會(huì)出現(xiàn)的類型(通常不建議使用)
let num:never;
num = (()=>{
throw new Error("錯(cuò)誤");
})();
聯(lián)合類型
即允許多個(gè)類型同時(shí)存在
let val:number|string;
val = 123;
val = "Hello";
交叉類型
使用 & 符號(hào)將多個(gè)類型疊加到一起成為的一種新的類型,基包含了多個(gè)類型的所有特性。
interface Student {
id: string;
age: number;
}
interface Worker {
companyId: string;
}
type A = Student & Worker; // A 此時(shí)包含了 Student 和 Worker 特性
let x: A;
x.age = 5; // 合法
x.companyId = 'CID5241'; // 合法
x.id = 'ID3241'; // 合法
// 合并對(duì)象演示
function extend<T, U>(first: T, second: U): T & U {
let result = {} as T & U // result 是要返回結(jié)果,類型斷言為 T&U
for(let id in first){ // 不能將類型 T 分配給類型 T&U,故需使用斷言
result[id] = first[id] as any
}
for(let id in second){
result[id] = second[id] as any
}
return result; // 返回結(jié)果,類型是 T & U
}
class A{
constructor(public name: string){}
}
class B{
constructor(public id:number,public age:number){}
}
let c = extend(new A('Hello'), new B(100,12));
c.name; // 'Hello'
c.id; // 100
c.age; // 12
斷言
斷言不是類型轉(zhuǎn)換,通過斷言告訴編譯器『我完全知道自己在干什么』
// 尖括號(hào)語(yǔ)法
let val: any = "this is a string";
let len: number = (<string>val).length;
// as語(yǔ)法
let val: any = "this is a string";
let len: number = (val as string).length;
顯示賦值斷言
顯式賦值斷言是 TS2.7 引入的一個(gè)新語(yǔ)法。明確告訴編譯器:該屬性或變量已經(jīng)賦過值了。
// 用在類屬性上
class C {
foo!: number;
constructor() {
this.initialize();
}
initialize() {
this.foo = 0;
}
}
// 用在變量上
let x!: number[];
initialize();
x.push(4);
function initialize() {
x = [0, 1, 2, 3];
}
函數(shù)
// 參數(shù)和返回值類型都要指定
function run(a:number, b:number):string{
return a+b.toString();
}
// 可選參數(shù)
function run(a:number, b?:number):string{
let val = b ? b : a;
return val.toString();
}
// 默認(rèn)參數(shù)
function run(a:number=100):string{
return a.toString();
}
// 剩余參數(shù)(擴(kuò)展運(yùn)算符來接收參數(shù))
function run(...arg:number[]):number{
let result = 0;
for(let i:number=0; i<arg.length; i++){
result += arg[i];
};
return result;
}
// 函數(shù)重載(即多個(gè)同名函數(shù),但參數(shù)不一樣。即會(huì)出現(xiàn)函數(shù)重載)
function run(a:string):string;
function run(a:number):string;
function run(a:string|number){
return typeof a === 'string' ? "字符" : "數(shù)字";
};
run("Hello"); // 字符
run(100); // 數(shù)字
類
/*
類的三個(gè)修飾符
---
public 公有 在類、子類、類外都可訪問(默認(rèn))
protected 保護(hù) 在類、子類可訪問
priveate 私有 在類可訪問,子類、類外都不可訪問
*/
// 類的基本結(jié)構(gòu)
class Person{
name:string; // 屬性,前面省略了 public 關(guān)鍵詞
constructor(a:string){ // 構(gòu)造函數(shù)
}
run():void{ // 類的方法,前面省略了 public 關(guān)鍵詞
console.log(this.name);
}
static run():void{ // 靜態(tài)方法,通過 Person.run 調(diào)用
}
}
// 類的繼承
class Person1 extends Person {
constructor(a:string){
super(a); // 調(diào)用父類的構(gòu)造函數(shù)
}
work():void{ // 子類自己的方法
}
}
// 多態(tài),即父類定義一個(gè)方法不去實(shí)現(xiàn),讓繼承它的子類去實(shí)現(xiàn),每個(gè)子類有不同的表現(xiàn)
class Animal{
name:string;
constructor(a:string){
}
eat():void{ // 定義一個(gè)方法讓子類去實(shí)現(xiàn)
}
}
class Dog extends Animal{
constructor(a:string){
super(a); // 調(diào)用父類的構(gòu)造函數(shù)
}
eat():void{ // 子類自己實(shí)現(xiàn)的方法
console.log("吃骨頭");
}
}
// 抽象類,即只供其它類繼承使用基類,本身不能被實(shí)例化。使用 abstract 關(guān)鍵字定義抽象類和抽象方法
abstract class Animal{
name:string;
constructor(a:string){
this.name = a;
}
abstract eat():void; // 定義一個(gè)方法讓子類去實(shí)現(xiàn),abstract 方法只可在 abstract 類中
}
class Dog extends Animal{
constructor(a:string){
super(a); // 調(diào)用父類的構(gòu)造函數(shù)
}
eat():void{ // 子類中必須實(shí)現(xiàn)父類中定義的抽象方法
console.log("吃骨頭");
}
}
存取器
class A {
constructor(){}
private _name:string | undefined;
set name(str:string) {
this._name = str;
}
get name():string{
return `My name is ${this._name}`;
}
}
let a = new A();
a.name = "Andy";
a.name; // "My name is Andy"
三斜線指令
僅能放在文件的最頂端才可生效。
/// <reference path="..." />
// 常用于引入 `.d.ts`文件。用于告訴編譯器在編譯時(shí)要引入的額外文件
/// <reference types="node" />
// 引入到聲明文件,表明這個(gè)文件使用了 @types/node/index.d.ts 里面聲明的名字
/// <amd-module name="moduleName" />
// 用于指定 AMD 模塊的名稱(否則默認(rèn)為匿名)
/// <reference no-default-lib="true"/>
// 標(biāo)記為默認(rèn)庫(kù),lib.d.ts 文件就有此項(xiàng)
/// <reference lib="es2017.string" />
// 等效于使用 -lib es2017.string 進(jìn)行編譯
接口
規(guī)范的定義,用于起到限制的作用。接口不關(guān)心內(nèi)部狀態(tài)數(shù)據(jù),也不關(guān)心實(shí)現(xiàn)細(xì)節(jié)。只是限制必須提供某些方法。類于 java,同時(shí)增加了接口類型、函數(shù)、可索引、類等。
// 屬性接口
interface FullName {
firstName:string;
secondName?:string; // 可選屬性,該參數(shù)可傳可不傳
}
function fun(name:FullName):void{
console.log(name.firstName);
}
let obj = { // 傳入的參數(shù)必須包含 firstName、secondName是可選的
firstName:"張",
secondName:"三"
};
fun(obj);
// 函數(shù)類型接口(對(duì)方法傳入的參數(shù)以及返回值進(jìn)行約束)
interface encrypt {
(key:string, value:string):string;
}
let md5:encrypt = function(key:string, value:string):string{
return key+value;
}
md5("a","b");
// 可索引接口(約束數(shù)組、對(duì)象)
interface UserArr{ // 約束數(shù)組
[index:number]:string;
}
let arr:UserArr=['a','b'];
interface UserObj{ // 約束對(duì)象
[index:string]:string
}
let obj:UserObj={name:'a'}
// 類的類型接口(對(duì)類的約束,和抽象類相似)
interface Animal{ // 約束類
name:string;
eat(str:string):void;
}
class Dog implements Animal { // 實(shí)現(xiàn)接口
name:string;
constructor(name:string){
this.name = name;
}
eat(str:string):void{
}
}
// 接口擴(kuò)展(接口繼承接口)
interface Animal{
eat():void;
}
interface Person extends Animal { // 繼承接口
work():void;
}
class Web implements Person{ // 實(shí)現(xiàn)接口
name:string;
constructor(name:string){
this.name = name;
}
eat():void{
}
work():void{
}
}
泛型
解決類、接口方法的復(fù)制用,并對(duì)不特定數(shù)據(jù)類型支持。
// T即是泛型,具體什么類型是調(diào)用這個(gè)方法的時(shí)候決定的
function fun<T>(value:T):T{ // 定義了傳入與返回的類型一至
return value;
}
fun<number>(123); // 傳入的是數(shù)字,返回也必須是數(shù)字
// 泛型類
class Min<T>{
list:T[]=[];
add(value:T):void{
this.list.push(value);
}
min():T{
let minVal = this.list[0];
for(let i:number=0; i<this.list.length; i++){
if(minVal > this.list[i]){
minVal=this.list[i];
};
};
return minVal;
}
}
let m1 = new Min<number>(); // 實(shí)例化時(shí)才決定了 T 的類型為數(shù)字
m1.add(1);
m1.add(2);
m1.min(); // 1
let m2 = new Min<string>(); // 實(shí)例化時(shí)才決定了 T 的類型為字符
m2.add("a");
m2.add("b");
m2.min(); // "a"
// 泛型接口(方式一)
interface Config{ // 定義的泛型接口
<T>(value:T):T;
}
let fun:Config = function<T>(value:T):T { // 方法實(shí)現(xiàn)跟接口一致
return value;
}
fun<string>("a"); // "a",使用時(shí)才決定了其類型
fun<number>(1); // 1,使用時(shí)才決定了其類型
// 泛型接口(方式二)
interface Config<T>{ // 定義泛型接口
(value:T):T;
}
function fun<T>(value:T):T{ // 方法實(shí)現(xiàn)跟接口一致
return value;
}
let f:Config<string>=fun; // 決定了其類型
fun("a"); // "a"
混入 Mixins
類繼承之外的一種通過可重用組件創(chuàng)建類的方式。通過把類當(dāng)成接口,又不實(shí)現(xiàn)其定義的方法并具有混入類的屬性及方法。
class Mixin1{ // 混入類1
constructor(public name:string){}
log1():void{
console.log("方法一",this.name);
}
}
class Mixin2{ // 混入類2
constructor(public age:number){}
log2():void{
console.log("方法二",this.age);
}
}
class A implements Mixin1,Mixin2 { // 把 Mixin1、Mixin2 當(dāng)成接口
name!:string; // 定義占位,因?yàn)橐獙?shí)現(xiàn)接口中的屬性
age!:number; // 定義占位,因?yàn)橐獙?shí)現(xiàn)接口中的屬性
log1!:()=>void; // 定義占位,因?yàn)橐獙?shí)現(xiàn)接口中的方法
log2!:()=>void; // 定義占位,因?yàn)橐獙?shí)現(xiàn)接口中的方法
constructor(name:string, age:number){
this.name = name;
this.age = age;
}
log():void{
console.log("自身的方法");
}
}
// 混入方法,遍歷 Mixins 的所有方法、屬性,并復(fù)制到定義的占位目標(biāo)上去
function applyMixins(base:any, mixins:any[]){
mixins.forEach(mixin => {
Object.getOwnPropertyNames(mixin.prototype).forEach(name => {
base.prototype[name] = mixin.prototype[name];
});
});
}
// 應(yīng)用混入
applyMixins(A,[Mixin1,Mixin2]);
let a = new A("Andy",12);
a.log();
a.log1();
a.log2();
文件模塊
// export 可多次調(diào)用
// module.ts
export function module1():any[]{
return ["a"]
};
export function module2():string{
return "a";
};
// 或
function module1():any[]{
return ["a"]
};
function module2():string{
return "a";
};
export {module1, module2}; // 統(tǒng)一暴露
// main.ts
import {module1, module2 as m2} form "./module";
module1(); // ["a"]
m2(); // "a"
// export.default 每個(gè)文件內(nèi)只能用一次
// module.ts
export.default function():void{
}
// main.ts
import module form "./module";
module();
命名空間
與 Java、c# 基本一致
// 命名空間下的接口、類默認(rèn)均為私有
namespace A{
interface Animal {
name:string;
eat():void;
}
// 使用 export 暴露出去才可在外部調(diào)用
export class Dog implements Animal{
name:string;
constructor(name:string){
this.name = name;
}
eat(){
}
}
}
let d = new A.Dog("2ha");
d.eat();
// 命名空間也可以使用 export 暴露出去被外部調(diào)用
export namespace A{
interface Animal {
name:string;
eat():void;
}
export class Dog implements Animal{
name:string;
constructor(name:string){
this.name = name;
}
eat(){
}
}
}
裝飾器
ES7的標(biāo)準(zhǔn),裝飾器本身就是一個(gè)方法。裝飾器只能用于類、類的方法之上(可多個(gè))。普通函數(shù)不能使用,因?yàn)槠胀ê瘮?shù)存在變量提升。
// 類的裝飾器方法
function logClass(str:string){
return function(target:Function){
target.prototype.url = str;
}
}
@logClass("http://a")
class HttpClient{
url?:string;
constructor(){
}
getData():void{
}
}
let http:HttpClient = new HttpClient();
http.url; // "http://a"
// 屬性裝飾器
function logProperty(str:string){
return function(target:any, name:string){
target[name] = str; // target 為類的原型對(duì)象
}
}
class HttpClient{
@logProperty("a")
url:string|undefined;
constructor(){
}
getData():void{
}
}
let http:HttpClient = new HttpClient();
console.log(http.url) // "a"
// 方法裝飾器(用得較多)
function logFun(str:string){
return function(target:any,name:string,des:PropertyDescriptor):PropertyDescriptor{
let oldValue:Function = target[name];
// 修改方法。注意:ES5 、ES6的描述內(nèi)容有差別
des.value = function(){
console.log("調(diào)用了裝飾器");
return oldValue.call(this,str);
}
return des;
}
}
class HttpClient{
constructor(){
}
@logFun("a")
getData(str:string):string{
return str;
}
}
let http:HttpClient = new HttpClient();
console.log(http.getData("b")) // "a"
// 方法參數(shù)裝飾器(常用來擴(kuò)展類,使用場(chǎng)景較少)
function logParams(str:string){
return function(target:any,name:string,index:Number):void{
console.log(target); // target 類的原型鏈
console.log(name); // name 方法名稱
console.log(index); // index 參數(shù)的索引
}
}
class HttpClient{
constructor(){
}
getData(@logParams("uuid") uid:string){
}
}
執(zhí)行順序:屬性裝飾器 > 方法裝飾器 > 方法參數(shù)裝飾器 > 類裝飾器,同一類型裝飾器有多個(gè)則是從后往前執(zhí)行。
Javascript類型檢查
TS在2.3之后版本可以使用 --checkJs 對(duì) .js 文件進(jìn)行類型檢查和錯(cuò)誤提示。
可以通過添加注釋來控制檢查規(guī)則,部分規(guī)則如下:
// @ts-nocheck 忽略類型檢查
// @ts-check 則檢查某些.js文件
// @ts-ignore 忽略本行錯(cuò)誤
https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html