TypeScript 快速入門
1. TypeScript 簡介
1.1 初識(shí) TypeScript
1.1.1 TypeScript 的介紹
TypeScript是一種由微軟開發(fā)的開源泰讽、跨平臺(tái)的編程語言。它是JavaScript的超集恬吕,最終會(huì)被編譯為JavaScript代碼。
2012年10月,微軟發(fā)布了首個(gè)公開版本的TypeScript攒巍,2013年6月19日,在經(jīng)歷了一個(gè)預(yù)覽版之后微軟正式發(fā)布了正式版TypeScript
TypeScript的作者是安德斯·海爾斯伯格,C#的首席架構(gòu)師破镰。它是開源和跨平臺(tái)的編程語言。
TypeScript擴(kuò)展了JavaScript的語法压储,所以任何現(xiàn)有的JavaScript程序可以運(yùn)行在TypeScript環(huán)境中啤咽。
TypeScript是為大型應(yīng)用的開發(fā)而設(shè)計(jì),并且可以編譯為JavaScript渠脉。
TypeScript 是 JavaScript 的一個(gè)超集,主要提供了類型系統(tǒng)和對(duì) ES6+ 的支持**瓶佳,它由 Microsoft 開發(fā)芋膘,代碼開源于 GitHub 上
TypeScript 是 JavaScript 的一個(gè)超集,主要提供了類型系統(tǒng)和對(duì) ES6+ 的支持霸饲,它由 Microsoft 開發(fā)为朋,代碼開源于 GitHub 上
1.1.2 TypeScript 的特點(diǎn)
TypeScript 主要有 3 大特點(diǎn):
- 始于JavaScript,歸于JavaScript
TypeScript 可以編譯出純凈厚脉、 簡潔的 JavaScript 代碼习寸,并且可以運(yùn)行在任何瀏覽器上、Node.js 環(huán)境中和任何支持 ECMAScript 3(或更高版本)的JavaScript 引擎中傻工。
- 強(qiáng)大的類型系統(tǒng)
類型系統(tǒng)允許 JavaScript 開發(fā)者在開發(fā) JavaScript 應(yīng)用程序時(shí)使用高效的開發(fā)工具和常用操作比如靜態(tài)檢查和代碼重構(gòu)霞溪。
- 先進(jìn)的 JavaScript
TypeScript 提供最新的和不斷發(fā)展的 JavaScript 特性,包括那些來自 2015 年的 ECMAScript 和未來的提案中的特性中捆,比如異步功能和 Decorators鸯匹,以幫助建立健壯的組件。
1.1.3 總結(jié)
TypeScript 在社區(qū)的流行度越來越高泄伪,它非常適用于一些大型項(xiàng)目殴蓬,也非常適用于一些基礎(chǔ)庫,極大地幫助我們提升了開發(fā)效率和體驗(yàn)蟋滴。
1.2 安裝 TypeScript
命令行運(yùn)行如下命令染厅,全局安裝 TypeScript:
npm install -g typescript
安裝完成后痘绎,在控制臺(tái)運(yùn)行如下命令,檢查安裝是否成功
tsc -V
1.3. 編寫第一個(gè) TypeScript 程序
1.3.1 編寫 TS 程序
src/helloworld.ts
function greeter (person) {
return 'Hello, ' + person
}
let user = 'Yee'
console.log(greeter(user))
1.3.2 手動(dòng)編譯代碼
我們使用了 .ts
擴(kuò)展名肖粮,但是這段代碼僅僅是 JavaScript 而已孤页。
在命令行上,運(yùn)行 TypeScript 編譯器:
tsc helloworld.ts
輸出結(jié)果為一個(gè) helloworld.js
文件尿赚,它包含了和輸入文件中相同的 JavsScript 代碼散庶。
在命令行上,通過 Node.js 運(yùn)行這段代碼:
node helloworld.js
控制臺(tái)輸出:
Hello, Yee
1.3.3 VSCode 自動(dòng)編譯
1). 生成配置文件tsconfig.json
tsc --init
2). 修改tsconfig.json配置
"outDir": "./js",
"strict": false,
3). 啟動(dòng)監(jiān)視任務(wù):
終端 -> 運(yùn)行任務(wù) -> 監(jiān)視tsconfig.json
1.3.4 類型注釋
接下來讓我們看看 TypeScript 工具帶來的高級(jí)功能凌净。 給 person
函數(shù)的參數(shù)添加 : string
類型注解悲龟,如下:
// person 這個(gè)參數(shù)的類型是 string 類型的
function greeter (person: string) {
return 'Hello, ' + person
}
let user = 'Yee'
console.log(greeter(user))
TypeScript 里的類型注解是一種輕量級(jí)的為函數(shù)或變量添加約束的方式。 在這個(gè)例子里冰寻,我們希望 greeter
函數(shù)接收一個(gè)字符串參數(shù)须教。 然后嘗試把 greeter
的調(diào)用改成傳入一個(gè)數(shù)組:
function greeter (person: string) {
return 'Hello, ' + person
}
let user = [0, 1, 2]
console.log(greeter(user))
重新編譯,你會(huì)看到產(chǎn)生了一個(gè)錯(cuò)誤:
error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'.
類似地斩芭,嘗試刪除 greeter
調(diào)用的所有參數(shù)轻腺。 TypeScript 會(huì)告訴你使用了非期望個(gè)數(shù)的參數(shù)調(diào)用了這個(gè)函數(shù)。 在這兩種情況中划乖,TypeScript提供了靜態(tài)的代碼分析贬养,它可以分析代碼結(jié)構(gòu)和提供的類型注解。
要注意的是盡管有錯(cuò)誤琴庵,greeter.js
文件還是被創(chuàng)建了误算。 就算你的代碼里有錯(cuò)誤,你仍然可以使用 TypeScript迷殿。但在這種情況下儿礼,TypeScript 會(huì)警告你代碼可能不會(huì)按預(yù)期執(zhí)行。
1.3.5 接口
讓我們繼續(xù)擴(kuò)展這個(gè)示例應(yīng)用庆寺。這里我們使用接口來描述一個(gè)擁有 firstName
和 lastName
字段的對(duì)象蚊夫。 在 TypeScript
里,只在兩個(gè)類型內(nèi)部的結(jié)構(gòu)兼容懦尝,那么這兩個(gè)類型就是兼容的知纷。 這就允許我們?cè)趯?shí)現(xiàn)接口時(shí)候只要保證包含了接口要求的結(jié)構(gòu)就可以,而不必明確地使用 implements
語句
interface Person {
firstName: string
lastName: string
}
function greeter (person: Person) {
return 'Hello, ' + person.firstName + ' ' + person.lastName
}
let user = {
firstName: 'Yee',
lastName: 'Huang'
}
console.log(greeter(user))
1.3.6 類
最后导披,讓我們使用類來改寫這個(gè)例子屈扎。 TypeScript 支持 JavaScript 的新特性,比如支持基于類的面向?qū)ο缶幊獭?/p>
讓我們創(chuàng)建一個(gè) User
類撩匕,它帶有一個(gè)構(gòu)造函數(shù)和一些公共字段鹰晨。因?yàn)轭惖淖侄伟私涌谒枰淖侄危运麄兡芎芎玫募嫒荨?/p>
還要注意的是,我在類的聲明上會(huì)注明所有的成員變量模蜡,這樣比較一目了然漠趁。
class User {
fullName: string
firstName: string
lastName: string
constructor (firstName: string, lastName: string) {
this.firstName = firstName
this.lastName = lastName
this.fullName = firstName + ' ' + lastName
}
}
interface Person {
firstName: string
lastName: string
}
function greeter (person: Person) {
return 'Hello, ' + person.firstName + ' ' + person.lastName
}
let user = new User('Yee', 'Huang')
console.log(greeter(user))
重新運(yùn)行 tsc greeter.ts
,你會(huì)看到 TypeScript 里的類只是一個(gè)語法糖忍疾,本質(zhì)上還是 JavaScript
函數(shù)的實(shí)現(xiàn)闯传。
2. TypeScript常用語法
2.1 基礎(chǔ)類型
TypeScript 支持與 JavaScript 幾乎相同的數(shù)據(jù)類型,此外還提供了實(shí)用的枚舉類型方便我們使用卤妒。
2.1.1 布爾值 boolean
最基本的數(shù)據(jù)類型就是簡單的 true/false 值甥绿,在JavaScript 和 TypeScript 里叫做 boolean
(其它語言中也一樣)。
// 布爾類型
// 基本語法
// let 變量名: 數(shù)據(jù)類型 = 值
let isDone: boolean = false;
isDone = true;
// isDone = 2 // error
2.1.2 數(shù)字 number
和 JavaScript 一樣则披,TypeScript 里的所有數(shù)字都是浮點(diǎn)數(shù)共缕。 這些浮點(diǎn)數(shù)的類型是 number。 除了支持十進(jìn)制和十六進(jìn)制字面量士复,TypeScript 還支持 ECMAScript 2015中引入的二進(jìn)制和八進(jìn)制字面量图谷。
// 數(shù)字類型
let a1: number = 10 // 十進(jìn)制
let a2: number = 0b1010 // 二進(jìn)制, 以 0b 開頭
let a3: number = 0o12 // 八進(jìn)制, 以 0o 開頭
let a4: number = 0xa // 十六進(jìn)制, 以 0x 開頭
2.1.3 字符串 string
JavaScript 程序的另一項(xiàng)基本操作是處理網(wǎng)頁或服務(wù)器端的文本數(shù)據(jù)。 像其它語言里一樣阱洪,我們使用 string
表示文本數(shù)據(jù)類型便贵。 和 JavaScript 一樣,可以使用雙引號(hào)("
)或單引號(hào)('
)表示字符串冗荸。
let name:string = 'tom'
name = 'jack'
// name = 12 // error
let age:number = 12
const info = `My name is ${name}, I am ${age} years old!`
注意:
字符串和數(shù)字之間能夠一起拼接
Ts中的變量一開始是什么類型, 那么后賦值的時(shí)候, 只能用這個(gè)類型的數(shù)據(jù), 是允許用其他類型的數(shù)據(jù)賦值給當(dāng)前的這個(gè)變量中
2.1.4 undefined 和 null
TypeScript 里承璃,undefined
和 null
兩者各自有自己的類型分別叫做 undefined
和 null
。 它們的本身的類型用處不是很大:
let u: undefined = undefined
let n: null = null
默認(rèn)情況下 null
和 undefined
是所有類型的子類型蚌本。 就是說你可以把 null
和 undefined
賦值給 number
類型的變量(在非嚴(yán)格模式下)绸硕。
2.1.5 數(shù)組
TypeScript 像 JavaScript 一樣可以操作數(shù)組元素。 有兩種方式可以定義數(shù)組魂毁。 第一種,可以在元素類型后面接上[]
出嘹,表示由此類型元素組成的一個(gè)數(shù)組:
// 數(shù)組定義方式1
// let 變量名: 數(shù)據(jù)類型[] = [值1, 值2, 值3]
let list1: number[] = [1, 2, 3]
第二種方式是使用數(shù)組泛型席楚,Array<元素類型>
// 數(shù)組定義方式2: 泛型寫法
// let 變量名: Array<數(shù)據(jù)類型> = [值1, 值2, 值3]
let list2: Array<number> = [1, 2, 3]
注意:
數(shù)組定義后, 里面的數(shù)據(jù)類型必須和定義數(shù)組的時(shí)候類型是一致的, 否則有錯(cuò)誤信息, 也不會(huì)編譯通過
2.1.6 元組 Tuple
元組類型允許表示一個(gè)已知元素?cái)?shù)量和類型的數(shù)組,各元素的類型不必相同
税稼。 比如烦秩,你可以定義一對(duì)值分別為 string
和 number
類型的元組。
let t1: [string, number]
t1 = ['hello', 10] // OK
t1 = [10, 'hello'] // Error
// 注意: 元組類型在使用的時(shí)候, 數(shù)據(jù)類型的位置和數(shù)據(jù)的個(gè)數(shù), 應(yīng)該和在定義元組的時(shí)候的數(shù)據(jù)類型以及位置應(yīng)該是一致的
當(dāng)訪問一個(gè)已知索引的元素郎仆,會(huì)得到正確的類型:
console.log(t1[0].substring(1)) // OK
console.log(t1[1].substring(1)) // Error, 'number' 不存在 'substring' 方法
2.1.7 枚舉
enum
類型是對(duì) JavaScript 標(biāo)準(zhǔn)數(shù)據(jù)類型的一個(gè)補(bǔ)充只祠。 使用枚舉類型可以為一組數(shù)值賦予友好的名字
。
// 枚舉類型, 枚舉里面的每個(gè)數(shù)據(jù)值都可以叫元素, 每個(gè)元素都有自己的, 編號(hào)從0開始, 依次遞增加一
enum Color {
Red,
Green,
Blue
}
// 枚舉數(shù)值默認(rèn)從0開始依次遞增
// 根據(jù)特定的名稱得到對(duì)應(yīng)的枚舉數(shù)值
let myColor: Color = Color.Green // 0
console.log(myColor, Color.Red, Color.Blue)
默認(rèn)情況下扰肌,從 0
開始為元素編號(hào)抛寝。 你也可以手動(dòng)的指定成員的數(shù)值。 例如,我們將上面的例子改成從 1
開始編號(hào):
enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green
可以用 key 去換 value, 也可以用 value 去換 key
或者盗舰,全部都采用手動(dòng)賦值:
enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green
枚舉類型提供的一個(gè)便利是你可以由枚舉的值得到它的名字晶府。 例如,我們知道數(shù)值為 2钻趋,但是不確定它映射到 Color 里的哪個(gè)名字川陆,我們可以查找相應(yīng)的名字:
enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2]
console.log(colorName) // 'Green'
枚舉中的元素也可以是中文的數(shù)據(jù)值, 但是不推薦
2.1.8 any
有時(shí)候,我們會(huì)想要為那些在編程階段還不清楚類型的變量指定一個(gè)類型蛮位。 這些值可能來自于動(dòng)態(tài)的內(nèi)容较沪,比如來自用戶輸入或第三方代碼庫。 這種情況下失仁,我們不希望類型檢查器對(duì)這些值進(jìn)行檢查而是直接讓它們通過編譯階段的檢查尸曼。 那么我們可以使用 any
類型來標(biāo)記這些變量:
let notSure: any = 4
notSure = 'maybe a string'
notSure = false // 也可以是個(gè) boolean
在對(duì)現(xiàn)有代碼進(jìn)行改寫的時(shí)候,any
類型是十分有用的陶因,它允許你在編譯時(shí)可選擇地包含或移除類型檢查骡苞。并且當(dāng)你只知道一部分?jǐn)?shù)據(jù)的類型時(shí),any
類型也是有用的楷扬。 比如解幽,你有一個(gè)數(shù)組,它包含了不同的類型的數(shù)據(jù):
// 當(dāng)一個(gè)數(shù)組中要存儲(chǔ)多個(gè)數(shù)據(jù), 個(gè)數(shù)不確定, 類型不確定, 此時(shí)也可以使用 any 類型來定義數(shù)組
let list: any[] = [1, true, 'free']
list[1] = 100
// any 類型導(dǎo)致沒有靜態(tài)類型檢查
2.1.9 void
某種程度上來說烘苹,void
類型像是與 any
類型相反躲株,它表示沒有任何類型
。 當(dāng)一個(gè)函數(shù)沒有返回值時(shí)镣衡,你通常會(huì)見到其返回值類型是 void
:
/* 表示沒有任何類型, 一般用來說明函數(shù)的返回值不能是undefined和null之外的值 */
function fn(): void {
console.log('fn()')
// return undefined
// return null
// return 1 // error
}
聲明一個(gè) void
類型的變量沒有什么大用霜定,因?yàn)槟阒荒転樗x予 undefined
和 null
:
let unusable: void = undefined
2.1.10 object
object
表示非原始類型,也就是除 number
廊鸥,string
望浩,boolean
之外的類型。
使用 object
類型惰说,就可以更好的表示像 Object.create
這樣的 API
磨德。例如:
function fn2(obj:object):object {
console.log('fn2()', obj)
return {}
// return undefined
// return null
}
console.log(fn2(new String('abc')))
// console.log(fn2('abc') // error
console.log(fn2(String))
2.1.11 聯(lián)合類型
聯(lián)合類型(Union Types)表示取值可以為多種類型中的一種
需求1: 定義一個(gè)一個(gè)函數(shù)得到一個(gè)數(shù)字或字符串值的字符串形式值
function toString2(x: number | string) : string {
return x.toString()
}
需求2: 定義一個(gè)一個(gè)函數(shù)得到一個(gè)數(shù)字或字符串值的長度
function getLength(x: number | string) {
// return x.length // error
if (x.length) { // error
return x.length
} else {
return x.toString().length
}
}
2.1.12 類型斷言
通過類型斷言這種方式可以告訴編譯器,“相信我吆视,我知道自己在干什么”典挑。 類型斷言好比其它語言里的類型轉(zhuǎn)換,但是不進(jìn)行特殊的數(shù)據(jù)檢查和解構(gòu)啦吧。 它沒有運(yùn)行時(shí)的影響您觉,只是在編譯階段起作用。 TypeScript 會(huì)假設(shè)你授滓,程序員琳水,已經(jīng)進(jìn)行了必須的檢查肆糕。
類型斷言有兩種形式。 其一是“尖括號(hào)”語法, 另一個(gè)為 as
語法
/*
類型斷言(Type Assertion): 可以用來手動(dòng)指定一個(gè)值的類型
語法:
方式一: <類型>值
方式二: 值 as 類型 tsx中只能用這種方式
*/
/* 需求: 定義一個(gè)函數(shù)得到一個(gè)字符串或者數(shù)值數(shù)據(jù)的長度 */
function getLength(x: number | string) {
if ((<string>x).length) {
return (x as string).length
} else {
return x.toString().length
}
}
console.log(getLength('abcd'), getLength(1234))
2.1.13 類型推斷
類型推斷: TS會(huì)在沒有明確的指定類型的時(shí)候推測(cè)出一個(gè)類型
有下面2種情況: 1. 定義變量時(shí)賦值了, 推斷為對(duì)應(yīng)的類型. 2. 定義變量時(shí)沒有賦值, 推斷為any類型
/* 定義變量時(shí)賦值了, 推斷為對(duì)應(yīng)的類型 */
let b9 = 123 // number
// b9 = 'abc' // error
/* 定義變量時(shí)沒有賦值, 推斷為any類型 */
let b10 // any類型
b10 = 123
b10 = 'abc'
2.2 接口
TypeScript 的核心原則之一是對(duì)值所具有的結(jié)構(gòu)進(jìn)行類型檢查炫刷。我們使用接口(Interfaces)來定義對(duì)象的類型擎宝。接口是對(duì)象的狀態(tài)(屬性)和行為(方法)的抽象(描述)
2.2.1 接口初探
需求: 創(chuàng)建人的對(duì)象, 需要對(duì)人的屬性進(jìn)行一定的約束
id是number類型, 必須有, 只讀的
name是string類型, 必須有
age是number類型, 必須有
sex是string類型, 可以沒有
下面通過一個(gè)簡單示例來觀察接口是如何工作的:
/*
在 TypeScript 中,我們使用接口(Interfaces)來定義對(duì)象的類型
接口: 是對(duì)象的狀態(tài)(屬性)和行為(方法)的抽象(描述)
接口類型的對(duì)象
多了或者少了屬性是不允許的
可選屬性: ?
只讀屬性: readonly
*/
/*
需求: 創(chuàng)建人的對(duì)象, 需要對(duì)人的屬性進(jìn)行一定的約束
id是number類型, 必須有, 只讀的
name是string類型, 必須有
age是number類型, 必須有
sex是string類型, 可以沒有
*/
// 定義人的接口
interface IPerson {
id: number
name: string
age: number
sex: string
}
const person1: IPerson = {
id: 1,
name: 'tom',
age: 20,
sex: '男'
}
類型檢查器會(huì)查看對(duì)象內(nèi)部的屬性是否與IPerson接口描述一致, 如果不一致就會(huì)提示類型錯(cuò)誤浑玛。
2.2.2 可選屬性
接口里的屬性不全都是必需的绍申。 有些是只在某些條件下存在,或者根本不存在顾彰。
interface IPerson {
id: number
name: string
age: number
sex?: string
}
帶有可選屬性的接口與普通的接口定義差不多极阅,只是在可選屬性名字定義的后面加一個(gè) ?
符號(hào)。
可選屬性的好處之一是可以對(duì)可能存在的屬性進(jìn)行預(yù)定義涨享,好處之二是可以捕獲引用了不存在的屬性時(shí)的錯(cuò)誤筋搏。
const person2: IPerson = {
id: 1,
name: 'tom',
age: 20,
// sex: '男' // 可以沒有
}
2.2.3 只讀屬性
一些對(duì)象屬性只能在對(duì)象剛剛創(chuàng)建的時(shí)候修改其值。 你可以在屬性名前用 readonly
來指定只讀屬性:
interface IPerson {
// id 是只讀的, 是 number 類型, const 修飾屬性, 想要設(shè)置該屬性是只讀的, 不能使用 const
readonly id: number
name: string
age: number
// ? 可有可無的
sex?: string
}
一旦賦值后再也不能被改變了厕隧。
const person2: IPerson = {
id: 2,
name: 'tom',
age: 20,
// sex: '男' // 可以沒有
// xxx: 12 // error 沒有在接口中定義, 不能有
}
person2.id = 2 // error
readonly vs const
最簡單判斷該用 readonly
還是 const
的方法是看要把它做為變量使用還是做為一個(gè)屬性奔脐。 做為變量使用的話用 const
,若做為屬性則使用 readonly
吁讨。
2.2.4 函數(shù)類型
接口能夠描述 JavaScript 中對(duì)象擁有的各種各樣的外形髓迎。 除了描述帶有屬性的普通對(duì)象外,接口也可以描述函數(shù)類型建丧。
為了使用接口表示函數(shù)類型排龄,我們需要給接口定義一個(gè)調(diào)用簽名。它就像是一個(gè)只有參數(shù)列表和返回值類型的函數(shù)定義翎朱。參數(shù)列表里的每個(gè)參數(shù)都需要名字和類型橄维。
/*
接口可以描述函數(shù)類型(參數(shù)的類型與返回的類型)
*/
// 定義一個(gè)接口, 用來作為某個(gè)函數(shù)的類型使用
interface SearchFunc {
// 定義一個(gè)調(diào)用簽名
(source: string, subString: string): boolean
}
// 定義一個(gè)函數(shù), 該類型就是上面定義的接口
const mySearch: SearchFunc = function (source: string, sub: string): boolean {
// 在 source 字符串中查找 subString 這個(gè)字符串
return source.search(sub) > -1
}
const mySearch2: SearchFunc = function (source, sub): boolean {
// 在 source 字符串中查找 subString 這個(gè)字符串
return source.search(sub) > -1
}
console.log(mySearch('abcd', 'bc'))
2.2.5 類類型
與 C# 或 Java 里接口的基本作用一樣,TypeScript 也能夠用它來明確的強(qiáng)制一個(gè)類去符合某種契約拴曲。
/*
類類型: 實(shí)現(xiàn)接口
1. 一個(gè)類可以實(shí)現(xiàn)多個(gè)接口
2. 一個(gè)接口可以繼承多個(gè)接口
*/
interface Alarm {
// 該方法沒有任何的實(shí)現(xiàn)(方法中什么都沒有)
alert(): any;
}
interface Light {
lightOn(): void;
lightOff(): void;
}
class Car implements Alarm {
// 實(shí)現(xiàn)接口中的方法
alert() {
console.log('Car alert');
}
}
// 實(shí)例化對(duì)象
const car = new Car()
car.alert()
2.2.6 一個(gè)類可以實(shí)現(xiàn)多個(gè)接口
class Car2 implements Alarm, Light {
alert() {
console.log('Car alert');
}
lightOn() {
console.log('Car light on');
}
lightOff() {
console.log('Car light off');
}
}
2.2.7 接口繼承接口
和類一樣争舞,接口也可以相互繼承。 這讓我們能夠從一個(gè)接口里復(fù)制成員到另一個(gè)接口里澈灼,可以更靈活地將接口分割到可重用的模塊里兑障。
interface LightableAlarm extends Alarm, Light {
}
總結(jié)
類可以通過接口的方式, 來定義當(dāng)前這個(gè)類的類型
類可以實(shí)現(xiàn)一個(gè)接口, 類也可以實(shí)現(xiàn)多個(gè)接口, 要注意, 接口中的內(nèi)容都要真正的實(shí)現(xiàn)
接口和接口之間叫繼承(使用 extend關(guān)鍵字), 類和接口之間叫實(shí)現(xiàn)(使用的是implements)
2.3 類
對(duì)于傳統(tǒng)的 JavaScript 程序我們會(huì)使用函數(shù)
和基于原型的繼承
來創(chuàng)建可重用的組件,但對(duì)于熟悉使用面向?qū)ο蠓绞降某绦騿T使用這些語法就有些棘手蕉汪,因?yàn)樗麄冇玫氖?code>基于類的繼承并且對(duì)象是由類構(gòu)建出來的。 從 ECMAScript 2015逞怨,也就是 ES6 開始者疤, JavaScript 程序員將能夠使用基于類的面向?qū)ο蟮姆绞健?使用 TypeScript,我們?cè)试S開發(fā)者現(xiàn)在就使用這些特性叠赦,并且編譯后的 JavaScript 可以在所有主流瀏覽器和平臺(tái)上運(yùn)行驹马,而不需要等到下個(gè) JavaScript 版本革砸。
2.3.1 基本示例
下面看一個(gè)使用類的例子:
/*
類的基本定義與使用
*/
class Greeter {
// 聲明屬性
message: string
// 構(gòu)造方法, 為了將來實(shí)例化對(duì)象的時(shí)候, 可以直接對(duì)屬性的值進(jìn)行初始化, 可以設(shè)置默認(rèn)值
constructor (message: string='哈哈哈') {
this.message = message
}
// 一般方法, 定義實(shí)例方法
greet (): string {
return 'Hello ' + this.message
}
}
// 創(chuàng)建類的實(shí)例
const greeter = new Greeter('world')
// 調(diào)用實(shí)例的方法
console.log(greeter.greet())
如果你使用過 C# 或 Java,你會(huì)對(duì)這種語法非常熟悉糯累。 我們聲明一個(gè) Greeter
類算利。這個(gè)類有 3 個(gè)成員:一個(gè)叫做 message
的屬性,一個(gè)構(gòu)造函數(shù)和一個(gè) greet
方法泳姐。
你會(huì)注意到效拭,我們?cè)谝萌魏我粋€(gè)類成員的時(shí)候都用了 this
。 它表示我們?cè)L問的是類的成員胖秒。
后面一行缎患,我們使用 new
構(gòu)造了 Greeter
類的一個(gè)實(shí)例。它會(huì)調(diào)用之前定義的構(gòu)造函數(shù)阎肝,創(chuàng)建一個(gè) Greeter
類型的新對(duì)象挤渔,并執(zhí)行構(gòu)造函數(shù)初始化它。
最后一行通過 greeter
對(duì)象調(diào)用其 greet
方法
2.3.2 繼承
在 TypeScript 里风题,我們可以使用常用的面向?qū)ο竽J健?基于類的程序設(shè)計(jì)中一種最基本的模式是允許使用繼承來擴(kuò)展現(xiàn)有的類判导。
/*
類的繼承: 繼承是 類與類之間的關(guān)系
*/
class Animal {
run (distance: number) {
console.log(`Animal run ${distance}m`)
}
}
class Dog extends Animal {
cry () {
console.log('wang! wang!')
}
}
const dog = new Dog()
dog.cry()
dog.run(100) // 可以調(diào)用從父中繼承得到的方法
這個(gè)例子展示了最基本的繼承:類從基類中繼承了屬性和方法。 這里沛硅,Dog
是一個(gè) 派生類眼刃,它派生自 Animal
基類,通過 extends
關(guān)鍵字稽鞭。 派生類通常被稱作子類鸟整,基類通常被稱作超類。
總結(jié)
類和類之間如果要有繼承關(guān)系, 需要使用 extend 關(guān)鍵字
子類中可以調(diào)用父類中的構(gòu)造函數(shù), 使用 super 關(guān)鍵字 (包括調(diào)用父類中的示例方法, 可以使用super)
子類可以重寫父類的方法
因?yàn)?Dog
繼承了 Animal
的功能朦蕴,因此我們可以創(chuàng)建一個(gè) Dog
的實(shí)例篮条,它能夠 cry()
和 run()
。
下面我們來看個(gè)更加復(fù)雜的例子吩抓。
class Animal {
name: string
constructor (name: string) {
this.name = name
}
run (distance: number=0) {
console.log(`${this.name} run ${distance}m`)
}
}
class Snake extends Animal {
constructor (name: string) {
// 調(diào)用父類型構(gòu)造方法
super(name)
}
// 重寫父類型的方法
run (distance: number=5) {
console.log('sliding...')
super.run(distance)
}
}
class Horse extends Animal {
constructor (name: string) {
// 調(diào)用父類型構(gòu)造方法
super(name)
}
// 重寫父類型的方法
run (distance: number=50) {
console.log('dashing...')
// 調(diào)用父類型的一般方法
super.run(distance)
}
xxx () {
console.log('xxx()')
}
}
const snake = new Snake('sn')
snake.run()
const horse = new Horse('ho')
horse.run()
// 父類型引用指向子類型的實(shí)例 ==> 多態(tài)
const tom: Animal = new Horse('ho22')
tom.run()
/* 如果子類型沒有擴(kuò)展的方法, 可以讓子類型引用指向父類型的實(shí)例 */
const tom3: Snake = new Animal('tom3')
tom3.run()
/* 如果子類型有擴(kuò)展的方法, 不能讓子類型引用指向父類型的實(shí)例 */
// const tom2: Horse = new Animal('tom2')
// tom2.run()
這個(gè)例子展示了一些上面沒有提到的特性涉茧。 這一次,我們使用 extends
關(guān)鍵字創(chuàng)建了 Animal的兩個(gè)子類:Horse
和 Snake
疹娶。
與前一個(gè)例子的不同點(diǎn)是伴栓,派生類包含了一個(gè)構(gòu)造函數(shù),它 必須調(diào)用 super()
雨饺,它會(huì)執(zhí)行基類的構(gòu)造函數(shù)钳垮。 而且,在構(gòu)造函數(shù)里訪問 this
的屬性之前额港,我們 一定要調(diào)用 super()
饺窿。 這個(gè)是 TypeScript 強(qiáng)制執(zhí)行的一條重要規(guī)則。
這個(gè)例子演示了如何在子類里可以重寫父類的方法移斩。Snake
類和 Horse
類都創(chuàng)建了 run
方法肚医,它們重寫了從 Animal
繼承來的 run
方法绢馍,使得 run
方法根據(jù)不同的類而具有不同的功能。注意肠套,即使 tom
被聲明為 Animal
類型舰涌,但因?yàn)樗闹凳?Horse
,調(diào)用 tom.run(34)
時(shí)你稚,它會(huì)調(diào)用 Horse
里重寫的方法瓷耙。
sliding...
sn run 5m
dashing...
ho run 50m
2.3.3 公共, 私有與受保護(hù)的修飾符
默認(rèn)為 public
在上面的例子里,我們可以自由的訪問程序里定義的成員入宦。 如果你對(duì)其它語言中的類比較了解哺徊,就會(huì)注意到我們?cè)谥暗拇a里并沒有使用 public
來做修飾;例如乾闰,C# 要求必須明確地使用 public
指定成員是可見的落追。 在 TypeScript 里,成員都默認(rèn)為 public
涯肩。
你也可以明確的將一個(gè)成員標(biāo)記成 public
轿钠。 我們可以用下面的方式來重寫上面的 Animal
類:
理解 private
當(dāng)成員被標(biāo)記成 private
時(shí),它就不能在聲明它的類的外部訪問病苗。
理解 protected
protected
修飾符與 private
修飾符的行為很相似疗垛,但有一點(diǎn)不同,protected
成員在派生類中仍然可以訪問硫朦。例如:
/*
訪問修飾符: 用來描述類內(nèi)部的屬性/方法的可訪問性
public: 默認(rèn)值, 公開的外部也可以訪問
private: 只能類內(nèi)部可以訪問
protected: 類內(nèi)部和子類可以訪問
*/
class Animal {
public name: string
public constructor (name: string) {
this.name = name
}
public run (distance: number=0) {
console.log(`${this.name} run ${distance}m`)
}
}
class Person extends Animal {
private age: number = 18
protected sex: string = '男'
run (distance: number=5) {
console.log('Person jumping...')
super.run(distance)
}
}
class Student extends Person {
run (distance: number=6) {
console.log('Student jumping...')
console.log(this.sex) // 子類能看到父類中受保護(hù)的成員
// console.log(this.age) // 子類看不到父類中私有的成員
super.run(distance)
}
}
console.log(new Person('abc').name) // 公開的可見
// console.log(new Person('abc').sex) // 受保護(hù)的不可見
// console.log(new Person('abc').age) // 私有的不可見
2.3.4 readonly修飾符
你可以使用 readonly
關(guān)鍵字將屬性設(shè)置為只讀的贷腕。 只讀屬性必須在聲明時(shí)或構(gòu)造函數(shù)里被初始化。
readonly 是一個(gè)關(guān)鍵字, 對(duì)類中的屬性成員進(jìn)行修飾, 修飾符后, 該屬性成員, 就不能在外部被隨意修改了
類中的普通方法中, 也是不能修改 readonly修飾的成員屬性
類中的構(gòu)造函數(shù)中, 可以對(duì)只讀的屬性成員的數(shù)據(jù)進(jìn)行修改
如果構(gòu)造函數(shù)中沒有任何參數(shù), 類中的屬性成員此時(shí)已經(jīng)使用 readonly進(jìn)行修飾了, 則外部也是不能對(duì)這個(gè)屬性值進(jìn)行修改的
class Person {
readonly name: string = 'abc'
constructor(name: string) {
this.name = name
}
}
let john = new Person('John')
// john.name = 'peter' // error
參數(shù)屬性
在上面的例子中咬展,我們必須在 Person
類里定義一個(gè)只讀成員 name
和一個(gè)參數(shù)為 name
的構(gòu)造函數(shù)泽裳,并且立刻將 name
的值賦給 this.name
,這種情況經(jīng)常會(huì)遇到破婆。 參數(shù)屬性可以方便地讓我們?cè)谝粋€(gè)地方定義并初始化一個(gè)成員涮总。 下面的例子是對(duì)之前 Person
類的修改版,使用了參數(shù)屬性:
class Person2 {
constructor(readonly name: string) {
}
}
const p = new Person2('jack')
console.log(p.name)
構(gòu)造函數(shù)中的 name 參數(shù), 一旦使用 readonly 進(jìn)行修飾后, 那么該 name 參數(shù)可以叫參數(shù)屬性
構(gòu)造函數(shù)中的 name 參數(shù), 一旦使用 readonly 進(jìn)行修飾后, 那么 Person 中就有了一個(gè) name 的屬性成員
構(gòu)造函數(shù)中的 name 參數(shù), 一旦使用 readonly 進(jìn)行修飾后, 外部也是無法修改類中的name屬性成員值的
構(gòu)造函數(shù)中的 name 參數(shù), 一旦使用 public 進(jìn)行修飾后, 那么 Person 類中就有一個(gè)公共的 name 屬性成員了
構(gòu)造函數(shù)中的 name 參數(shù), 一旦使用 private 進(jìn)行修飾后, 那么 Person 類中就有一個(gè)私有的 name 屬性成員了
構(gòu)造函數(shù)中的 name 參數(shù), 一旦使用 protected 進(jìn)行修飾后, 那么 Person 類中就有一個(gè)私有受保護(hù)的 name 屬性成員了(只能在本類和派生類中訪問及使用)
2.3.5 存取器
TypeScript
支持通過 getters/setters
來截取對(duì)對(duì)象成員的訪問祷舀。 它能幫助你有效的控制對(duì)對(duì)象成員的訪問瀑梗。
下面來看如何把一個(gè)簡單的類改寫成使用 get
和 set
。 首先裳扯,我們從一個(gè)沒有使用存取器的例子開始抛丽。
class Person {
firstName: string = 'A'
lastName: string = 'B'
// 讀取器
get fullName () {
return this.firstName + '-' + this.lastName
}
// 設(shè)置器
set fullName (value) {
const names = value.split('-')
this.firstName = names[0]
this.lastName = names[1]
}
}
const p = new Person()
console.log(p.fullName)
p.firstName = 'C'
p.lastName = 'D'
console.log(p.fullName)
p.fullName = 'E-F'
console.log(p.firstName, p.lastName)
2.3.6 靜態(tài)屬性
到目前為止,我們只討論了類的實(shí)例成員饰豺,那些僅當(dāng)類被實(shí)例化的時(shí)候才會(huì)被初始化的屬性亿鲜。 我們也可以創(chuàng)建類的靜態(tài)成員,這些屬性存在于類本身上面而不是類的實(shí)例上哟忍。 在這個(gè)例子里狡门,我們使用 static
定義 origin
,因?yàn)樗撬芯W(wǎng)格都會(huì)用到的屬性锅很。 每個(gè)實(shí)例想要訪問這個(gè)屬性的時(shí)候其馏,都要在 origin
前面加上類名。 如同在實(shí)例屬性上使用 this.xxx
來訪問屬性一樣爆安,這里我們使用 Grid.xxx
來訪問靜態(tài)屬性叛复。
/*
靜態(tài)屬性, 是類對(duì)象的屬性
非靜態(tài)屬性, 是類的實(shí)例對(duì)象的屬性
*/
// 靜態(tài)成員在使用的時(shí)候是通過 類名. 的這種語法來調(diào)用的
// 類匯總
class Person {
name1: string = 'A'
static name2: string = 'B'
}
console.log(Person.name2)
console.log(new Person().name1)
2.3.7 抽象類
抽象類做為其它派生類的基類使用。 它們不能被實(shí)例化扔仓。不同于接口褐奥,抽象類可以包含成員的實(shí)現(xiàn)細(xì)節(jié)。 abstract
關(guān)鍵字是用于定義抽象類和在抽象類內(nèi)部定義抽象方法翘簇。
/*
抽象類
不能創(chuàng)建實(shí)例對(duì)象, 只有實(shí)現(xiàn)類才能創(chuàng)建實(shí)例
可以包含未實(shí)現(xiàn)的抽象方法
*/
abstract class Animal {
// 抽象方法, 不能有具體實(shí)現(xiàn)
abstract cry ()
run () {
console.log('run()')
}
}
class Dog extends Animal {
cry () {
console.log(' Dog cry()')
}
}
const dog = new Dog()
dog.cry()
dog.run()
2.4 函數(shù)
函數(shù)是 JavaScript 應(yīng)用程序的基礎(chǔ)撬码,它幫助你實(shí)現(xiàn)抽象層,模擬類版保,信息隱藏和模塊呜笑。在 TypeScript 里,雖然已經(jīng)支持類彻犁,命名空間和模塊叫胁,但函數(shù)仍然是主要的定義行為的地方。TypeScript 為 JavaScript 函數(shù)添加了額外的功能汞幢,讓我們可以更容易地使用驼鹅。
2.4.1 基本示例
和 JavaScript 一樣,TypeScript 函數(shù)可以創(chuàng)建有名字的函數(shù)和匿名函數(shù)森篷。你可以隨意選擇適合應(yīng)用程序的方式输钩,不論是定義一系列 API 函數(shù)還是只使用一次的函數(shù)。
通過下面的例子可以迅速回想起這兩種 JavaScript 中的函數(shù):
// 命名函數(shù)
function add(x, y) {
return x + y
}
// 匿名函數(shù)
let myAdd = function(x, y) {
return x + y;
}
2.4.2 函數(shù)類型
為函數(shù)定義類型
讓我們?yōu)樯厦婺莻€(gè)函數(shù)添加類型:
function add(x: number, y: number): number {
return x + y
}
// 函數(shù)中 x 和 y參數(shù)的類型都是 string 類型的, 小括號(hào)后面的 :number 代表是該函數(shù)的返回值也是 number類型的
let myAdd = function(x: number, y: number): number {
return x + y
}
我們可以給每個(gè)參數(shù)添加類型之后再為函數(shù)本身添加返回值類型疾宏。TypeScript 能夠根據(jù)返回語句自動(dòng)推斷出返回值類型张足。
書寫完整函數(shù)類型 (一般用不上)
現(xiàn)在我們已經(jīng)為函數(shù)指定了類型,下面讓我們寫出函數(shù)的完整類型坎藐。
// myAdd2 ---> 變量名 ---> 函數(shù) add3
// (x: number, y: number) => number 當(dāng)前這個(gè)函數(shù)的類型
let myAdd2: (x: number, y: number) => number = function(x: number, y: number): number{
return x + y
}
2.4.3 可選參數(shù)和默認(rèn)參數(shù)
TypeScript 里的每個(gè)函數(shù)參數(shù)都是必須的为牍。 這不是指不能傳遞 null
或 undefined
作為參數(shù),而是說編譯器檢查用戶是否為每個(gè)參數(shù)都傳入了值岩馍。編譯器還會(huì)假設(shè)只有這些參數(shù)會(huì)被傳遞進(jìn)函數(shù)碉咆。 簡短地說,傳遞給一個(gè)函數(shù)的參數(shù)個(gè)數(shù)必須與函數(shù)期望的參數(shù)個(gè)數(shù)一致蛀恩。
JavaScript 里疫铜,每個(gè)參數(shù)都是可選的,可傳可不傳双谆。 沒傳參的時(shí)候壳咕,它的值就是 undefined
席揽。 在TypeScript 里我們可以在參數(shù)名旁使用 ?
實(shí)現(xiàn)可選參數(shù)的功能。 比如谓厘,我們想讓 lastName
是可選的:
在 TypeScript 里幌羞,我們也可以為參數(shù)提供一個(gè)默認(rèn)值當(dāng)用戶沒有傳遞這個(gè)參數(shù)或傳遞的值是 undefined
時(shí)。 它們叫做有默認(rèn)初始化值的參數(shù)竟稳。 讓我們修改上例属桦,把firstName
的默認(rèn)值設(shè)置為 "A"
。
// 可選參數(shù): 函數(shù)在聲明的時(shí)候, 內(nèi)部的參數(shù)使用了 ? 進(jìn)行修飾, 表示該參數(shù)可以傳入也可以不傳入, 叫可選參數(shù)
// 默認(rèn)參數(shù): 函數(shù)在聲明的時(shí)候, 內(nèi)部的參數(shù)有自己的默認(rèn)值, 此時(shí)的這個(gè)參數(shù)就可以叫默認(rèn)參數(shù)
function buildName(firstName: string='A', lastName?: string): string {
if (lastName) {
return firstName + '-' + lastName
} else {
return firstName
}
}
console.log(buildName('C', 'D'))
console.log(buildName('C'))
console.log(buildName())
剩余參數(shù)
必要參數(shù)他爸,默認(rèn)參數(shù)和可選參數(shù)有個(gè)共同點(diǎn):它們表示某一個(gè)參數(shù)聂宾。 有時(shí),你想同時(shí)操作多個(gè)參數(shù)诊笤,或者你并不知道會(huì)有多少參數(shù)傳遞進(jìn)來系谐。 在 JavaScript 里,你可以使用 arguments
來訪問所有傳入的參數(shù)盏混。
在 TypeScript 里蔚鸥,你可以把所有參數(shù)收集到一個(gè)變量里:
剩余參數(shù)會(huì)被當(dāng)做個(gè)數(shù)不限的可選參數(shù)。 可以一個(gè)都沒有许赃,同樣也可以有任意個(gè)止喷。 編譯器創(chuàng)建參數(shù)數(shù)組,名字是你在省略號(hào)( ...
)后面給定的名字混聊,你可以在函數(shù)體內(nèi)使用這個(gè)數(shù)組弹谁。
// 剩余參數(shù)(rest參數(shù))
// 剩余參數(shù)是放在函數(shù)聲明的時(shí)候所有的參數(shù)的最后
function info(x: string, ...args: string[]) {
console.log(x, args)
}
info('abc', 'c', 'b', 'a')
2.4.4 函數(shù)重載
函數(shù)重載: 函數(shù)名相同, 而形參不同的多個(gè)函數(shù)
在JS中, 由于弱類型的特點(diǎn)和形參與實(shí)參可以不匹配, 是沒有函數(shù)重載這一說的 但在TS中, 與其它面向?qū)ο蟮恼Z言(如Java)就存在此語法
/*
函數(shù)重載: 函數(shù)名相同, 而形參不同的多個(gè)函數(shù)
需求: 我們有一個(gè)add函數(shù),它可以接收2個(gè)string類型的參數(shù)進(jìn)行拼接句喜,也可以接收2個(gè)number類型的參數(shù)進(jìn)行相加
*/
// 重載函數(shù)聲明
function add (x: string, y: string): string
function add (x: number, y: number): number
// 定義函數(shù)實(shí)現(xiàn)
function add(x: string | number, y: string | number): string | number {
// 在實(shí)現(xiàn)上我們要注意嚴(yán)格判斷兩個(gè)參數(shù)的類型是否相等预愤,而不能簡單的寫一個(gè) x + y
if (typeof x === 'string' && typeof y === 'string') {
return x + y
} else if (typeof x === 'number' && typeof y === 'number') {
return x + y
}
}
console.log(add(1, 2))
console.log(add('a', 'b'))
// console.log(add(1, 'a')) // error
2.5 泛型
指在定義函數(shù)、接口或類的時(shí)候咳胃,不預(yù)先指定具體的類型植康,而在使用的時(shí)候再指定具體類型的一種特性。
2.5.1 引入
下面創(chuàng)建一個(gè)函數(shù), 實(shí)現(xiàn)功能: 根據(jù)指定的數(shù)量 count
和數(shù)據(jù) value
, 創(chuàng)建一個(gè)包含 count
個(gè) value
的數(shù)組 不用泛型的話展懈,這個(gè)函數(shù)可能是下面這樣:
function createArray(value: any, count: number): any[] {
const arr: any[] = []
for (let index = 0; index < count; index++) {
arr.push(value)
}
return arr
}
const arr1 = createArray(11, 3)
const arr2 = createArray('aa', 3)
console.log(arr1[0].toFixed(), arr2[0].split(''))
2.5.2 使用函數(shù)泛型
function createArray2 <T> (value: T, count: number) {
const arr: Array<T> = []
for (let index = 0; index < count; index++) {
arr.push(value)
}
return arr
}
const arr3 = createArray2<number>(11, 3)
console.log(arr3[0].toFixed())
// console.log(arr3[0].split('')) // error
const arr4 = createArray2<string>('aa', 3)
console.log(arr4[0].split(''))
// console.log(arr4[0].toFixed()) // error
2.5.3 多個(gè)泛型參數(shù)的函數(shù)
一個(gè)函數(shù)可以定義多個(gè)泛型參數(shù)
function swap <K, V> (a: K, b: V): [K, V] {
return [a, b]
}
const result = swap<string, number>('abc', 123)
console.log(result[0].length, result[1].toFixed())
2.5.4 泛型接口
在定義接口時(shí), 為接口中的屬性或方法定義泛型類型
在使用接口時(shí), 再指定具體的泛型類型
需求: 定義一個(gè)類, 用來存儲(chǔ)用戶的相關(guān)信息(id, 名字, 年齡)
通過一個(gè)類的示例對(duì)象調(diào)用 add方法可以添加多個(gè)用戶信息對(duì)象, 調(diào)用 getUserId 方法可以根據(jù) Id獲取某個(gè)指定的用戶信息對(duì)象
interface IbaseCRUD <T> {
data: T[]
add: (t: T) => void
getById: (id: number) => T
}
// 定義一個(gè)用戶信息的類
class User {
id?: number; //id主鍵自增, ?
name: string; //姓名
age: number; //年齡
// 構(gòu)造器
constructor (name, age) {
this.name = name
this.age = age
}
}
class UserCRUD implements IbaseCRUD <User> {
// 定義一個(gè)類, 可以針對(duì)用戶的信息對(duì)象進(jìn)行增加及查詢的操作
data: User[] = []
add(user: User): void {
user = {...user, id: Date.now()}
this.data.push(user)
console.log('保存user', user.id)
}
getById(id: number): User {
return this.data.find(item => item.id===id)
}
}
const userCRUD = new UserCRUD()
userCRUD.add(new User('tom', 12))
userCRUD.add(new User('tom2', 13))
console.log(userCRUD.data)
2.5.5 泛型類
在定義類時(shí), 為類中的屬性或方法定義泛型類型 在創(chuàng)建類的實(shí)例時(shí), 再指定特定的泛型類型
class GenericNumber<T> {
zeroValue: T
add: (x: T, y: T) => T
}
let myGenericNumber = new GenericNumber<number>()
myGenericNumber.zeroValue = 0
myGenericNumber.add = function(x, y) {
return x + y
}
let myGenericString = new GenericNumber<string>()
myGenericString.zeroValue = 'abc'
myGenericString.add = function(x, y) {
return x + y
}
console.log(myGenericString.add(myGenericString.zeroValue, 'test'))
console.log(myGenericNumber.add(myGenericNumber.zeroValue, 12))
2.5.6 泛型約束
如果我們直接對(duì)一個(gè)泛型參數(shù)取 length
屬性, 會(huì)報(bào)錯(cuò), 因?yàn)檫@個(gè)泛型根本就不知道它有這個(gè)屬性
// 沒有泛型約束
function fn <T>(x: T): void {
// console.log(x.length) // error
}
我們可以使用泛型約束來實(shí)現(xiàn)
interface Lengthwise {
length: number;
}
// 指定泛型約束
function fn2 <T extends Lengthwise>(x: T): void {
console.log(x.length)
}
我們需要傳入符合約束類型的值销睁,必須包含必須 length
屬性:
fn2('abc')
// fn2(123) // error number沒有l(wèi)ength屬性
2.6 其他
2.6.1 聲明文件
當(dāng)使用第三方庫時(shí),我們需要引用它的聲明文件存崖,才能獲得對(duì)應(yīng)的代碼補(bǔ)全冻记、接口提示等功能
什么是聲明語句
假如我們想使用第三方庫 jQuery,一種常見的方式是在 html 中通過 <script>
標(biāo)簽引入 jQuery
来惧,然后就可以使用全局變量 $
或 jQuery
了冗栗。
但是在 ts 中,編譯器并不知道 $ 或 jQuery 是什么東西
/*
當(dāng)使用第三方庫時(shí),我們需要引用它的聲明文件隅居,才能獲得對(duì)應(yīng)的代碼補(bǔ)全钠至、接口提示等功能。
聲明語句: 如果需要ts對(duì)新的語法進(jìn)行檢查, 需要要加載了對(duì)應(yīng)的類型說明代碼
declare var jQuery: (selector: string) => any;
聲明文件: 把聲明語句放到一個(gè)單獨(dú)的文件(jQuery.d.ts)中, ts會(huì)自動(dòng)解析到項(xiàng)目中所有聲明文件
下載聲明文件: npm install @types/jquery --save-dev
*/
jQuery('#foo');
// ERROR: Cannot find name 'jQuery'.
這時(shí)胎源,我們需要使用 declare var 來定義它的類型
declare var jQuery: (selector: string) => any;
jQuery('#foo');
declare var 并沒有真的定義一個(gè)變量棕洋,只是定義了全局變量 jQuery 的類型,僅僅會(huì)用于編譯時(shí)的檢查乒融,在編譯結(jié)果中會(huì)被刪除。它編譯結(jié)果是:
jQuery('#foo');
一般聲明文件都會(huì)單獨(dú)寫成一個(gè) xxx.d.ts
文件
創(chuàng)建 01_jQuery.d.ts
, 將聲明語句定義其中, TS編譯器會(huì)掃描并加載項(xiàng)目中所有的TS聲明文件
很多的第三方庫都定義了對(duì)應(yīng)的聲明文件庫, 庫文件名一般為 @types/xxx
, 可以在 https://www.npmjs.com/package/package
進(jìn)行搜索
有的第三庫在下載時(shí)就會(huì)自動(dòng)下載對(duì)應(yīng)的聲明文件庫(比如: webpack),有的可能需要單獨(dú)下載(比如jQuery/react)
2.6.2 內(nèi)置對(duì)象
JavaScript 中有很多內(nèi)置對(duì)象摄悯,它們可以直接在 TypeScript 中當(dāng)做定義好了的類型赞季。
內(nèi)置對(duì)象是指根據(jù)標(biāo)準(zhǔn)在全局作用域(Global)上存在的對(duì)象。這里的標(biāo)準(zhǔn)是指 ECMAScript 和其他環(huán)境(比如 DOM)的標(biāo)準(zhǔn)奢驯。
-
ECMAScript 的內(nèi)置對(duì)象
Boolean
Number
String
Date
RegExp
Error/* 1. ECMAScript 的內(nèi)置對(duì)象 */ let b: Boolean = new Boolean(1) let n: Number = new Number(true) let s: String = new String('abc') let d: Date = new Date() let r: RegExp = /^1/ let e: Error = new Error('error message') b = true // let bb: boolean = new Boolean(2) // error
-
BOM 和 DOM 的內(nèi)置對(duì)象
Window
Document
HTMLElement
DocumentFragment
Event
NodeListconst div: HTMLElement = document.getElementById('test') const divs: NodeList = document.querySelectorAll('div') document.addEventListener('click', (event: MouseEvent) => { console.dir(event.target) }) const fragment: DocumentFragment = document.createDocumentFragment()
轉(zhuǎn)自