前言:請先熟悉ES6,包括其中的import、export、class等。ts特性右轉(zhuǎn)百度,只討論入門語法,語法方面基本就是c++/java等靜態(tài)類型的語法簡化版,而且兼容js語法,有學(xué)過這些的話光速入門。
變量
1.let x: number = 6
其中:number說明x是一個number類型。也可以簡寫成let x = 6,typescript會自動推斷這些簡單類型
2.let list: number[] = [1, 2, 3]
表明list是一個數(shù)組,數(shù)組內(nèi)的元素只能是number類型。也可寫成let list: Array<number> = [1, 2, 3],這是typescript中泛型的寫法,等價的。下面的幾點(diǎn)均是對類型的進(jìn)一步說明
3.any代表任何類型,即跳過靜態(tài)檢查。如let x:any,那么就跟js中let x效果一模一樣,ts不會對其進(jìn)行檢查。
4.void用于沒有返回值的函數(shù)。一個函數(shù)返回類型為void時,只要使用了返回值就會報(bào)錯,而上面的any不會。
5.null,undefined可以賦值給任意類型的變量。(沒有使用--strictNullChecks時)
6.let a: number|string|object聯(lián)合類型。代表a既可以是number,也可以是string或object類型
7.接口、類均可作變量的類型聲明,但接口名可和變量重名,而類不行。
注:Number,Object等不要number這些類型“關(guān)鍵字”混作一談,后面會作詳解
函數(shù)
function add(x: number, y: number): number {
return x + y;
}
let myAdd = function(x: number, y?: number): number { return x; };
let myAdd = function(x: number, y: number = 12): number { return x + y; };
函數(shù)的參數(shù)類似變量聲明,:number加到后面即可規(guī)定參數(shù)類型。函數(shù)的括號()加上:number即可規(guī)定返回類型。
其中參數(shù)的后?代表該參數(shù)可選;給參數(shù)賦值y: number = 12代表參數(shù)默認(rèn)取12。即“可選參數(shù)”和“默認(rèn)參數(shù)”,效果和需要注意的細(xì)則右轉(zhuǎn)百度
接口
ts的接口與java等語言的不同,ts的接口只是用來規(guī)定對象、函數(shù)、類的“形狀”。
規(guī)定對象形狀
interface Example {
a: string;
b?: number;
readonly c: boolean;
}
let x: Example = {a:"123", c:true};//正確
x.c=false;//錯誤,c為只讀屬性
x = { a: "123", b:12, c: false };//正確
x = { a: "123", b:12, d:12};//兩個錯誤,缺少c屬性、不應(yīng)該具有d屬性
如上面中,變量x一定有a、c屬性,可能有b屬性,其中對c屬性只能讀不能改。
interface Example {
[propName: string]: number;
}
let x: Example;
x.a=1;//正確
x.b=2;//正確
x.c="123";//錯誤
這樣的接口表示對象可以有若干個“key類型為字符串、value類型為number”的屬性。
另一種可能情況是[propName: number]: number;,他表明你可以像x[0]=0;x[1]=1這樣使用。
且必須注意:
interface Example {
[propName: string]: number;
a:string;
}
這樣會報(bào)錯。如上面解釋,[propName: string]: number;規(guī)定x.a是number類型,而a:string;規(guī)定x.a是string類型,產(chǎn)生二義性導(dǎo)致報(bào)錯。
一個簡單解決方式是改為[propName: string]: any;,使其兼容(string是any的子類),詳細(xì)可右轉(zhuǎn)文檔
規(guī)定函數(shù)形狀
interface Example1 {
(a: number): boolean;
(a: number, b: number): boolean;
new (a: number, b: number): boolean;
a:number;
}
let m1: Example1, temp:any;
temp = function (a,b) {return true;}
temp.a=123;
m1 = temp;
let a = m1(1), b = m1(1, 2), c = new m1(1, 2);
interface Example2{
(a: number): boolean;
}
let m2: Example2 = function (a: number): boolean { return true; }
一個接口可以定義函數(shù)的多種形狀,如上面的Example1接口。
當(dāng)接口如Example2只規(guī)定了一種形狀時,變量m2直接賦值成這種類型的函數(shù)就行了。
當(dāng)接口如Example1規(guī)定了多種形狀時,變量m1無法直接簡單賦值,此時需要搞個any類型的變量temp,js怎么做的這個temp就怎么做,最后賦值給變量m1。
注意:
1.接口名可以和變量重名,即let Example1:Example1;是可行的。同理,在typescript中,Number、String這些,既是接口也是變量。建議去typescript官方庫看下Number具體是什么一回事。
2.接口可在不同地方重復(fù)定義,后面重復(fù)定義的接口內(nèi)容將會被合并原有接口。簡單來說就是接口具有靈活的擴(kuò)展性,你不必在一處地方完全定義一個接口,也可以自行對原有接口進(jìn)行補(bǔ)充。
類
寫法跟js的class幾乎一樣,下面簡要概括
class A {
a: string;
protected c: string;
constructor(a: string, public b: string) {
this.a= a;
}
m():string {
return this.a + this.b;
}
static mm():number { return 0; }
}
let x = new A("1","2");
類成員a直接寫在里面,訪問級別默認(rèn)是public,變量和函數(shù)同樣遵從上面講過的寫法。
其特點(diǎn):
1.構(gòu)造函數(shù)里面的參數(shù)public b: string:當(dāng)你在構(gòu)造函數(shù)的參數(shù)b前面加上public、protected、private中任意一個,就代表聲明了一個成員變量b,且把參數(shù)b的值賦給它。簡單來說,就是一種對成員變量定義+構(gòu)造函數(shù)中初始化的簡寫形式。
2.像java一樣,類可以單繼承(extends)一個類,實(shí)現(xiàn)(implements)多個接口;接口可以繼承多個接口。比較奇葩的是接口同樣可以繼承多個類(interface B extends A{}),不過建議盡量別這樣用。
3.類實(shí)現(xiàn)接口,僅代表類的實(shí)例部分被接口所約束,即類中必須重新聲明定義接口中出現(xiàn)的方法和成員。這表明我們無法直接通過接口,規(guī)定其實(shí)現(xiàn)類的static修飾的成員、函數(shù)以及構(gòu)造函數(shù)(constructor)。深入了解還請右轉(zhuǎn)文檔
命名空間
即namespace,作用類似與js的import/export。用的比較少,我就大概說說了。
import/export大家都熟了,就是一個js一個模塊,另外一個文件可以通過import,導(dǎo)入export導(dǎo)出的變量和成員。這個的稍微不足之處是限死了一個模塊只能在一個js文件里,不過我們也能通過額外寫個js模塊負(fù)責(zé)總的導(dǎo)入導(dǎo)出,其實(shí)也還行。
而typescript中的namespace,同樣可以看作是模塊化,不過除去了上述限制。你可以在一個ts文件里寫多個namespace,也可以把一個namespace分散在若干個ts文件中,更加靈活。
namespace ns {
const a = 1;
let b = 1;
export const c = a + b;
export class A { }
}
let x = ns.c, xx = new ns.A();
可以看作是
//f1.js
const a = 1;
let b = 1;
export const c = a + b;
export class A { }
//f2.js
import * as ns from './f1.js'
let x = ns.c, xx = new ns.A();
當(dāng)然只是個大概示意,為了說明namespace拿來干什么用,不要忘記其特性。
注意:需要通過特殊注釋來發(fā)現(xiàn)另一個文件中的namespace,如/// <reference path="Validation.ts" />這種形式,而且有順序先后的影響。深入了解請右轉(zhuǎn)文檔
聲明文件
即d.ts后綴那些ts文件。如果是全項(xiàng)目都用ts的,那么沒什么問題;但如果你是js、ts混雜的,ts中無法識別js中的東西,這就需要我們?nèi)檫@些js中的對象、函數(shù)、類等寫一份ts格式的聲明,否則ts中大量報(bào)錯未定義、類型不對等等東西。
介紹兩個關(guān)鍵字type和declare:
(1)type跟c++的type差不多,用于類型別名,包括聯(lián)合類型,如type NumStr=number|string
(2)declare基本上專門用于聲明文件,被其修飾的變量/函數(shù)/類的成員和函數(shù)均無法被初始化賦值。
當(dāng)然你要用在普通ts文件、或者聲明文件不用它也是可以的,只是某些情況下你必須使用,如declare const XXX;,如果不加上declare,ts會報(bào)錯常量未初始化,但實(shí)際上這個是來自js的常量,你不能在ts中重復(fù)初始化,否則編譯為js后實(shí)際運(yùn)行時就會報(bào)錯。
另外一個要點(diǎn)是declare namespace X{}來修飾命名空間X時,等價于用export declare來修飾其內(nèi)部的每一個變量:
//a.js
var X={a:1,b:2,c:3}
export X;
//a.d.ts
//方式1
declare namespace X {
var a: number;
var b: number;
var c: number;
}
//方式2
namespace X {
export declare var a: number;
export declare var b: number;
export declare var c: number;
}
//方式3
interface X{
a: number;
b: number;
c: number;
// ():number;
}
declare var X: X;
注意:聲明一個對象不一定要用namespace實(shí)現(xiàn),也可通過接口+聲明一個對象來實(shí)現(xiàn),副作用是多了一個基本無作用的接口,如方式3。但是,假如X是一個函數(shù)對象,即X能以let a=X()函數(shù)調(diào)用時,你基本上只能靠接口實(shí)現(xiàn)(你用any來搞也行),如方式3的注釋