安裝
npm i -D typescript
ts不能直接在瀏覽器環(huán)境執(zhí)行,需要先編譯成js文件。
文件編譯指令:
tsc [tsfile]
tsc [tsfile] -w 可以自動監(jiān)視改變并重新編譯
類型
number
string
boolean
字面量:聲明變量可能的值,不能是別的值或者超出字面量的范圍。let a: 10|11;a = 10;
any:任意值,可以被賦值不同類型的值。變量也可以賦值給其它類型的變量,不安全。
unknown:安全的any。可以是任何類型值。變量不允許直接被賦值給其它非unknown類型的變量。
void:空值null或者undefined。
never:不能是任何值。這種返回值一般用于系統(tǒng)報錯throw newError()
object:任意js對象。
array:數(shù)組。
tuple:固定長度數(shù)組。[1,2]聲明后就不能變長
enum:枚舉。enum{'a', 'b'}
變量聲明
let a: number;
a = 1;
let b: string = 'abc'
如果變量聲明同時直接賦值,則類型會默認對應(yīng)聲明,
let a = false
a = 123 // 提示錯誤
sum(a:number, b:number): number { // 聲明參數(shù)類型和函數(shù)返回值類型
return a + b
}
sum(1, '2') // 提示錯誤,但是編譯還是會過,可以通過配置讓編譯也不通過
sum(1, 2, 3) // 提示錯誤,參數(shù)個數(shù)不匹配
使用字面量聲明:類似于常量,聲明了一個或者多個后,后面的代碼賦值不能超出范圍。
let a: 'hello'; // a的值只能是'hello'不能聲明成別的
a = 'world' // 提示錯誤
let b: 'hello' | 'world'; // b的值只能是'hello'或者'world'
其他類型:
let a: any; // 不限制任何類型賦值
let b; // 如果聲明變量不指定類型也不直接賦值,則類型同any
開發(fā)中一般不允許使用any類型,因為any類型的變量可以賦值給任何類型變量,會出現(xiàn)嚴重的問題:
let a;
a = 10;
a = true;
let b: number;
b = a; // tsc編譯不報錯
如果真有這種業(yè)務(wù)場景,推薦使用unknown。unknown相當于是一種安全的any,也可以賦值任意類型,但是不能隨便賦值給其它類型的變量,直接復(fù)制會報錯,如果實現(xiàn)進行過類型檢查則可以賦值。
let a: unknown
a = 'hello'
let b: string
if (typeof a === 'string') { // 方式1
b = a
}
b = a as string // 方式2 使用斷言,斷言就是告訴編譯器,這個變量的類型一定為指定類型
b = <string>a // 方式3 另一種斷言
let a: object // js中一切皆對象,所以通常不會這樣使用,實際上開發(fā)者關(guān)注的應(yīng)該是對象的結(jié)構(gòu)(屬性方法什么的)
a = {}
a = function(){}
let b: {name: string, age?: number} // 這種使用方式比較推薦,使用b的時候必須設(shè)置name屬性,age可選
b = {name: 'sony'}
let b: {name: string, [propName: string]: any} // 這種方式也比較推薦,[propName: string]: any表示可以追加任意自定義屬性,propName是自定義的名字隨便寫,string表示屬性名類型為字符串
b = {name: 'sony', a: 12}
let a: Function // 聲明函數(shù),但是實際上沒人這么寫,沒什么意義,開發(fā)者關(guān)注的是函數(shù)的結(jié)構(gòu)(參數(shù)返回值什么的)
let a: (arg1: number, arg2: number) => number // 函數(shù)的聲明方式
let arr: Array<number> // 聲明一個數(shù)組,內(nèi)部元素類型為number
let arr: number[] // 作用和上面的等價
let arr: [number, number] // 聲明定長數(shù)組,賦值的時候必須一一對應(yīng),這種方式的優(yōu)勢在于性能比較好,分配固定的內(nèi)存空間就ok了
關(guān)于枚舉enum,它的使用意圖和“|”很類似,enum的優(yōu)勢在于兼顧了語義化和存儲空間。
例如聲明一個變量let a: '男'|'女',業(yè)務(wù)編碼使用的時候沒什么問題,但是數(shù)據(jù)庫或者數(shù)據(jù)傳遞的時候通常傾向于更小的空間占用,比如存0或者1這種值,enum就可以滿足這個需求。
開發(fā)中可以先定義一個enum類型的類,使用enum中的元素名就代表對應(yīng)的值(編譯器會默認賦值成0、1這種值)
enum Gender {
Male,
Female
}
let person: {name: string, gender: Gender}
person = {name: '張三', gender: Gender.Male} // 相當于gender: 0
另外,比較特殊的運算符“&”,聲明類型的時候表示同時滿足的意思:
let o: {name: string} & {age: number} // 表示變量o賦值的時候必須是一個對象并且同時有name和age屬性。
額外提一下ts中的“?”和“!”的用法:
- 作為運算符:“?”用在三目運算符中(例如a?b:c),“!”表示取反(例如!a)
- 參數(shù):“?”用在參數(shù)中表示可選項。例如function(a: string, b?: number)
- 成員變量:“?”表示可選項,“!”表示此項一定有值且不為null
- 安全鏈式調(diào)用:
?表示可能為null,如果為null就不往下執(zhí)行,場景:a.b?.c();
!表示一定不為null,強制讓編譯器通過安全檢查,場景:a.b!.c();或者a.b!.c = xxx
面向?qū)ο?/strong>
ts的面向?qū)ο笫褂闷饋砼ces6基本一致:
// class聲明和使用
class Animal {
static readonly type = '爬行動物' // 靜態(tài)屬性,readonly關(guān)鍵字表示只讀
name = 'lucky' // 實例屬性
// 構(gòu)造函數(shù)
constructor(name: string = 'lucky') {
this.name = name
}
// 實例方法,同樣,加了static就變成類靜態(tài)方法了
eat() {
console.log(this.name + ' is eating...')
}
static move() {
console.log('animal can move')
}
}
const cat = new Animal('happy')
console.log(cat, Animal.type) // {name: 'happy'} '爬行動物'
console.log('eat: ')
cat.eat()
console.log('move: ')
Animal.move()
class Dog extends Animal {
eat() {
console.log('dog: ' + this.name + ' is eating...')
}
}
console.log(Dog.type)
const dog = new Dog()
console.log(dog, Dog.type)
dog.eat()
interface Human {
name: string
eat(): void
speak(): void
}
class Soldier implements Human {
name: string = '001'
private _level: number
constructor(name: string, level: number) {
this.name = name
this._level = level
}
get level(): number {
return this._level
}
set level(value: number) {
this._level = value
}
eat(): void {
console.log(`soldier[level ${this.level}] ${this.name} is eating...`)
}
speak(): void {
console.log(`soldier[level ${this.level}] ${this.name} is speaking...`)
}
}
const orcSoldier = new Soldier('crom', 1)
orcSoldier.eat()
orcSoldier.speak()
// 泛型,生產(chǎn)中不允許使用any類型,如果確實有場景要應(yīng)對未知類型,或者類型是使用者自定義的,可以考慮使用泛型
function f<T, K>(t: T, k: K): T {
console.log(`t: ${t}, k: ${k}`)
return t
}
console.log(f(5, 3)) // 編譯器自動識別類型
console.log(f<string, number>('hello', 1)) // 手動指定類型,更加嚴謹,推薦方式
es6目前無法支持抽象類、接口、私有屬性修飾符private等面向?qū)ο蟮奶匦裕瑃s編譯器支持,但也只局限在檢查層面,比如對私有屬性賦值,雖然tsc檢查錯誤但是默認仍然可以正常編譯成js執(zhí)行,所以需要配置中"noEmitOnError": true 避免最終代碼出現(xiàn)邏輯漏洞。
編譯器配置
ts文件的需要先經(jīng)過編譯器執(zhí)行tsc指令編譯成js文件才能在瀏覽器環(huán)境執(zhí)行,開發(fā)過程中我們可以創(chuàng)建一個tconfig.json并根據(jù)需求配置參數(shù),編譯器會讀取配置文件并覆蓋默認配置。
ts配置文件跟普通json文件不太一樣,里面可以寫注釋。
{
/* include:用于編譯指定目錄下的ts文件,**表示任意目錄,*表示所有文件 */
"include": ["./src/**/*"],
/* exclude:不編譯某些目錄下的文件 */
/* extends:表示繼承某些配置文件,用于根據(jù)需要拆分組合配置 */
/* files:直接編譯指定文件名,用于小微型的應(yīng)用開發(fā),不常用 */
/* compailerOptions: 編譯配置項,可以定制編譯規(guī)則,可以讓代碼更嚴謹,減少程序出錯概率 */
"compilerOptions": {
// target表示編譯成什么樣的文件,具體指某個es版本,默認會編譯成es3,
// 通常會編譯成es6(es2015)再配合webpack的babel-loader處理
"target": "ES2015",
// 指定模塊化規(guī)范
"module": "ES2015",
// 指定用到了哪些庫,方便ts編譯器識別,比如用了dom就可以在代碼中使用document否則會編譯報錯。
// 這項通常不需要配置,默認配置已經(jīng)比較全。某些場景如nodejs環(huán)境或者用到一些默認不支持的lib會考慮配置
// "lib": ["DOM"]
// allowJS和checkJs一般成對使用,默認值是false,表示是否檢查和編譯js文件,
// 有些情況下可能出現(xiàn)js文件模塊和ts文件模塊編譯后無法正常使用的問題,這時候會需要把js文件也編譯處理一遍
"allowJs": true,
"checkJs": true,
"removeComments": true, // 是否移除注釋,默認是false
// "noEmit": false // 默認false,不生成編譯結(jié)果(target),不常用,某些情況下可能只是想使用編譯檢查功能的時候會設(shè)置這項
// 默認false,只要編譯出錯就不生成target,
// 在我們allowJs為true的時候,js文件即使檢查有錯誤,也會執(zhí)行編譯,這可能會出現(xiàn)錯誤隱患,
// 如果要嚴格控制js文件和ts一視同仁,可以設(shè)置為true
"noEmitOnError": true,
// 嚴格模式的代碼在瀏覽器環(huán)境下執(zhí)行效率更好,
// 通常模塊化引入的代碼默認都是嚴格模式,有些非模塊化代碼如果也需要嚴格模式(開頭加use strict),就需要配置此項
"alwaysStrict": true,
// 以下是一些常用代碼安全檢查規(guī)則
// 默認false,如果配置了strict為true,則所有規(guī)則選項(包括下面的選項)都會被覆蓋為嚴格模式,如果想訂制,則不要配置此項
// 開發(fā)中建議直接配置strict為true
// "strict": true,
// 不允許隱式any類型(不明確聲明類型),默認是false,any會導(dǎo)致編譯器不去判斷類型,造成代碼隱患
"noImplicitAny": true,
// 不允許隱式this(不明確聲明this是什么類型),默認是false,原生js中this的指向非常靈活,跟動態(tài)調(diào)用環(huán)境有關(guān)系(執(zhí)行上下文),
// 這也是bug隱患,如果是單純聲明一個function內(nèi)部想用this,可以考慮把“this”作為參數(shù)傳進來
"noImplicitThis": true,
// 是否嚴格檢查null值,默認false,有些操作如獲取dom,然后執(zhí)行dom.xxx()的時候,dom可能獲取失敗即是null,這樣執(zhí)行就會報錯,
// 為了避免這個隱患,編譯器可以配置這個選項為true,代碼中可以用if判斷包裹,也可以使用ts語法dom?.xxx()
"strictNullChecks": true
}
}
ts-loader
開發(fā)中我們一般會使用webpack打包代碼,使用ts-loader編譯ts文件再通過babel-loader解決js的兼容性問題
ts-loader使用的時候建議先到官網(wǎng)上看下版本兼容相關(guān)信息,8.x和9.x分別對應(yīng)webpack4 和webpack5
npm i -D ts-loader typescript
resolve: {
// Add `.ts` and `.tsx` as a resolvable extension.
extensions: [".ts", ".tsx", ".js"]
},
module: {
rules: [
// all files with a `.ts` or `.tsx` extension will be handled by `ts-loader`
{
test: /\.tsx?$/,
loader: "ts-loader",
exclude: /node_modules/
}
]
}
然后別忘了把tconfig.json放到項目根目錄下