初識(shí)TypeScript
TypeScript 的介紹
TypeScript是一種由微軟開發(fā)的開源印蔗、跨平臺(tái)的編程語(yǔ)言。它是JavaScript的超集丑勤,最終會(huì)被編譯為JavaScript代碼华嘹。
TypeScript擴(kuò)展了JavaScript的語(yǔ)法,所以任何現(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 主要有3 大特點(diǎn)
始于JavaScript秉剑,歸于JavaScript
TypeScript 可以編譯出純凈泛豪、 簡(jiǎn)潔的 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 特性略水,包括那些來(lái)自 2015 年的 ECMAScript 和未來(lái)的提案中的特性价卤,比如異步功能和 Decorators,以幫助建立健壯的組件渊涝。
總結(jié)
TypeScript 在社區(qū)的流行度越來(lái)越高慎璧,它非常適用于一些大型項(xiàng)目,也非常適用于一些基礎(chǔ)庫(kù)跨释,極大地幫助我們提升了開發(fā)效率和體驗(yàn)胸私。
安裝 TypeScript
命令行運(yùn)行如下命令,全局安裝 TypeScript:
npm install -g typescript
安裝完成后鳖谈,在控制臺(tái)運(yùn)行如下命令岁疼,檢查安裝是否成功(3.x):
tsc -V
第一個(gè) TypeScript 程序
編寫 TS 程序
src/hellowrld.ts
function greeter(person) {
? ? return'Hello,'+person
}
let user = 'Yee'
console.log(greeter(user))
手動(dòng)編譯代碼
我們使用了 .ts 擴(kuò)展名,但是這段代碼僅僅是 JavaScript 而已缆娃。
在命令行上(終端)捷绒,運(yùn)行 TypeScript 編譯器:
tsc hellworld.ts
輸出結(jié)果為一個(gè)helloword.js文件。包含了和輸入文件相同的JavaScript代碼
在命令行上贯要,通過(guò) Node.js 運(yùn)行這段代碼:
node helloworld.js
控制臺(tái)輸出:
Hello, Yee
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
類型注解
? TypeScript 工具帶來(lái)的高級(jí)功能暖侨。 給 person 函數(shù)的參數(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í)行笤休。
接口
讓我們繼續(xù)擴(kuò)展這個(gè)示例應(yīng)用尖飞。這里我們使用接口來(lái)描述一個(gè)擁有 firstName 和 lastName 字段的對(duì)象。 在 TypeScript 里店雅,只在兩個(gè)類型內(nèi)部的結(jié)構(gòu)兼容政基,那么這兩個(gè)類型就是兼容的。 這就允許我們?cè)趯?shí)現(xiàn)接口時(shí)候只要保證包含了接口要求的結(jié)構(gòu)就可以闹啦,而不必明確地使用 implements 語(yǔ)句沮明。
interface Person {
? ? firstName:sting
? ? lastName:string
}
function greeter(person:Person) {
? ? return 'Hello,'+person.firstName+' 'person.lastname
}
let user = {
? ? flastName:'Yee',
? ? lastName:'Huang'
}
console.log(greeter(user))
類
最后,讓我們使用類來(lái)改寫這個(gè)例子窍奋。 TypeScript 支持 JavaScript 的新特性荐健,比如支持基于類的面向?qū)ο缶幊獭?/p>
讓我們創(chuàng)建一個(gè) User 類,它帶有一個(gè)構(gòu)造函數(shù)和一些公共字段琳袄。因?yàn)轭惖淖侄伟私涌谒枰淖侄谓。运麄兡芎芎玫募嫒荨?/p>
還要注意的是,我在類的聲明上會(huì)注明所有的成員變量挚歧,這樣比較一目了然扛稽。
class USer {
? ? fullName:string
? ? fistName: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è)語(yǔ)法糖,本質(zhì)還是JavaScript實(shí)現(xiàn)
使用webpack打包TS
下載依賴
npm install -D typescript
npm install -D webpack webpack-cli
npm install -D webpack-dev-server
npm install -D html-webpack-plugin clean-webpack-plugin
npm install -D ts-loader
npm install cross-env
安裝依賴出現(xiàn)版本過(guò)高問(wèn)題滑负,參考下面鏈接
npm install -D html-webpack-plugin@4.5.0
npm install -D ts-loader@4.4.2
入口JS: src/main.ts
// import './01_helloworld'
document.write('Hello Webpack TS!')
index頁(yè)面: public/index.html
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="UTF-8">
<metaname="viewport"content="width=device-width, initial-scale=1.0">
<metahttp-equiv="X-UA-Compatible"content="ie=edge">
<title>webpack&TS</title>
</head>
<body>
</body>
</html>
build/webpack.config.js
const{CleanWebpackPlugin}=require('clean-webpack-plugin')
constHtmlWebpackPlugin=require('html-webpack-plugin')
constpath=require('path')
constisProd=process.env.NODE_ENV==='production'// 是否生產(chǎn)環(huán)境
functionresolve(dir) {
returnpath.resolve(__dirname,'..',dir)
}
module.exports={
mode:isProd?'production':'development',
entry: {
app:'./src/main.ts'
? },
output: {
path:resolve('dist'),
filename:'[name].[contenthash:8].js'
? },
module: {
rules: [
? ?? {
test:/\.tsx?$/,
use:'ts-loader',
include: [resolve('src')]
? ?? }
?? ]
? },
plugins: [
newCleanWebpackPlugin({
?? }),
newHtmlWebpackPlugin({
template:'./public/index.html'
?? })
? ],
resolve: {
extensions: ['.ts','.tsx','.js']
? },
devtool:isProd?'cheap-module-source-map':'cheap-module-eval-source-map',
devServer: {
host:'localhost',// 主機(jī)名
stats:'errors-only',// 打包日志輸出輸出錯(cuò)誤信息
port:8081,
open:true
? },
}
配置打包命令
"dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.js",
"build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js"
運(yùn)行與打包
yarn dev
yarn build
Typescript常用語(yǔ)法
基礎(chǔ)類型
布爾值
最基本的數(shù)據(jù)類型就是簡(jiǎn)單的 true/false 值在张,在JavaScript 和 TypeScript 里叫做 boolean(其它語(yǔ)言中也一樣)。
//? ? ? : 類型
letisDone:boolean=false;
isDone=true;
數(shù)字
和 JavaScript 一樣矮慕,TypeScript 里的所有數(shù)字都是浮點(diǎn)數(shù)帮匾。 這些浮點(diǎn)數(shù)的類型是 number。 除了支持十進(jìn)制和十六進(jìn)制字面量痴鳄,TypeScript 還支持 ECMAScript 2015中引入的二進(jìn)制和八進(jìn)制字面量瘟斜。
leta1:number=10// 十進(jìn)制
leta2:number=0b1010// 二進(jìn)制
leta3:number=0o12// 八進(jìn)制
leta4:number=0xa// 十六進(jìn)制
字符串
JavaScript 程序的另一項(xiàng)基本操作是處理網(wǎng)頁(yè)或服務(wù)器端的文本數(shù)據(jù)。 像其它語(yǔ)言里一樣,我們使用 string 表示文本數(shù)據(jù)類型螺句。 和 JavaScript 一樣虽惭,可以使用雙引號(hào)(")或單引號(hào)(')表示字符串。
letname:string='tom'
name='jack'
// name = 12 // error
letage:number=12
constinfo=`My name is ${name}, I am ${age}years old!`
總結(jié):ts中變量一開始是什么類型蛇尚,那么后期賦值的時(shí)候就只能用這個(gè)類型芽唇,不允許用其他類型的數(shù)據(jù)賦值給這個(gè)變量如?
let str:string = '你好'//可以
let str:string = 1234 //error
undefined和null
TypeScript 里,undefined 和 null 兩者各自有自己的類型分別叫做 undefined 和 null取劫。 它們的本身的類型用處不是很大:
letu:undefined=undefined
letn:null=null
默認(rèn)情況下 null 和 undefined 是所有類型的子類型匆笤。 就是說(shuō)你可以把 null 和 undefined 賦值給 number 類型的變量。
//需要將tsconfig.json 中的嚴(yán)格模式關(guān)閉
"strict": false,? //是否開啟嚴(yán)格模式?
數(shù)組
TypeScript 像 JavaScript 一樣可以操作數(shù)組元素谱邪。 有兩種方式可以定義數(shù)組炮捧。 第一種,可以在元素類型后面接上[]惦银,表示由此類型元素組成的一個(gè)數(shù)組:
//語(yǔ)法" let 變量名:數(shù)據(jù)類型[] = [值1咆课,值2,值3]
letlist1:number[]=[1,2,3]
第二種方式是使用數(shù)組泛型璧函,Array<元素類型>:
//語(yǔ)法:let 變量名:Array<數(shù)據(jù)類型> = [值1傀蚌,值2,值3]
letlist2:Array<number>=[1,2,3]
數(shù)組定義后蘸吓,里面的數(shù)據(jù)類型必須和定義數(shù)組時(shí)候的類型是一致的善炫,否則error
元組 Tuple
元組類型允許表示一個(gè)已知元素?cái)?shù)量和類型的數(shù)組,各元素的類型不必相同库继。 比如箩艺,你可以定義一對(duì)值分別為 string 和 number 類型的元組。
//在定義數(shù)組的時(shí)候宪萄,類型和數(shù)據(jù)的個(gè)數(shù)一開始就已經(jīng)限定了
lett1: [string,number]
t1=['hello',10]// OK
t1=[10,'hello']// Error
當(dāng)訪問(wèn)一個(gè)已知索引的元素艺谆,會(huì)得到正確的類型:
console.log(t1[0].substring(1))// OK
console.log(t1[1].substring(1))// Error, 'number' 不存在 'substring' 方法
枚舉
enum 類型是對(duì) JavaScript 標(biāo)準(zhǔn)數(shù)據(jù)類型的一個(gè)補(bǔ)充。 使用枚舉類型可以為一組數(shù)值賦予友好的名字拜英。
enumColor{
Red,
Green,
Blue
}
// 枚舉數(shù)值默認(rèn)從0開始依次遞增
// 根據(jù)特定的名稱得到對(duì)應(yīng)的枚舉數(shù)值
letmyColor:Color=Color.Green
console.log(myColor,Color.Red,Color.Blue)// 1 0 2
默認(rèn)情況下静汤,從 0 開始為元素編號(hào)。 你也可以手動(dòng)的指定成員的數(shù)值居凶。 例如虫给,我們將上面的例子改成從 1 開始編號(hào):
enumColor{Red=1,Green,Blue}
letc:Color=Color.Green
或者,全部都采用手動(dòng)賦值:
enumColor{Red=1,Green=2,Blue=4}
letc:Color=Color.Green
枚舉類型提供的一個(gè)便利是你可以由枚舉的值得到它的名字侠碧。 例如抹估,我們知道數(shù)值為 2,但是不確定它映射到 Color 里的哪個(gè)名字弄兜,我們可以查找相應(yīng)的名字:
enumColor{Red=1,Green,Blue}
letcolorName:string=Color[2]
console.log(colorName)// 'Green'
any
有時(shí)候药蜻,我們會(huì)想要為那些在編程階段還不清楚類型的變量指定一個(gè)類型瓷式。 這些值可能來(lái)自于動(dòng)態(tài)的內(nèi)容,比如來(lái)自用戶輸入或第三方代碼庫(kù)语泽。 這種情況下贸典,我們不希望類型檢查器對(duì)這些值進(jìn)行檢查而是直接讓它們通過(guò)編譯階段的檢查。 那么我們可以使用 any 類型來(lái)標(biāo)記這些變量:
letnotSure: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ù):
letlist:any[]=[1,true,'free']
list[1]=100
void
某種程度上來(lái)說(shuō),void 類型像是與 any 類型相反榨呆,它表示沒(méi)有任何類型罗标。 當(dāng)一個(gè)函數(shù)沒(méi)有返回值時(shí),你通常會(huì)見到其返回值類型是 void:
/* 表示沒(méi)有任何類型, 一般用來(lái)說(shuō)明函數(shù)的返回值不能是undefined和null之外的值 */
functionfn():void{
console.log('fn()')
// return undefined
// return null
// return 1 // error
}
聲明一個(gè) void 類型的變量沒(méi)有什么大用积蜻,因?yàn)槟阒荒転樗x予 undefined 和 null:
letunusable:void=undefined
object
object 表示非原始類型闯割,也就是除 number,string竿拆,boolean之外的類型宙拉。
使用 object 類型,就可以更好的表示像 Object.create 這樣的 API丙笋。例如:
functionfn2(obj:object):object{// 定義的參數(shù)是object類型 返回值也是object類型
console.log('fn2()',obj)
return{}
// return undefined
// return null
}
console.log(fn2(newString('abc')))
// console.log(fn2('abc') // error
console.log(fn2(String))
聯(lián)合類型
聯(lián)合類型(Union Types)表示取值可以為多種類型中的一種需求1: 定義一個(gè)一個(gè)函數(shù)得到一個(gè)數(shù)字或字符串值的字符串形式值
functiontoString2(x:number|string) :string{
returnx.toString()
}
需求2: 定義一個(gè)一個(gè)函數(shù)得到一個(gè)數(shù)字或字符串值的長(zhǎng)度
functiongetLength(x:number|string) {
// return x.length // error
if(x.length) {// error
returnx.length
}else{
returnx.toString().length
? }
}
類型斷言
通過(guò)類型斷言這種方式可以告訴編譯器谢澈,“相信我,我知道自己在干什么”御板。 類型斷言好比其它語(yǔ)言里的類型轉(zhuǎn)換锥忿,但是不進(jìn)行特殊的數(shù)據(jù)檢查和解構(gòu)。 它沒(méi)有運(yùn)行時(shí)的影響怠肋,只是在編譯階段起作用敬鬓。 TypeScript 會(huì)假設(shè)你丧肴,程序員夺谁,已經(jīng)進(jìn)行了必須的檢查。
類型斷言有兩種形式烛愧。 其一是“尖括號(hào)”語(yǔ)法, 另一個(gè)為 as 語(yǔ)法
/*
類型斷言(Type Assertion): 可以用來(lái)手動(dòng)指定一個(gè)值的類型
語(yǔ)法:
?? 方式一: <類型>值
?? 方式二: 值 as 類型? tsx中只能用這種方式
*/
/* 需求: 定義一個(gè)函數(shù)得到一個(gè)字符串或者數(shù)值數(shù)據(jù)的長(zhǎng)度 */
functiongetLength(x:number|string) {
if((<string>x).length) {
return(xasstring).length
}else{
returnx.toString().length
? }
}
console.log(getLength('abcd'),getLength(1234))
類型推斷
類型推斷: TS會(huì)在沒(méi)有明確的指定類型的時(shí)候推測(cè)出一個(gè)類型有下面2種情況: 1. 定義變量時(shí)賦值了, 推斷為對(duì)應(yīng)的類型. 2. 定義變量時(shí)沒(méi)有賦值, 推斷為any類型
/* 定義變量時(shí)賦值了, 推斷為對(duì)應(yīng)的類型 */
letb9=123// number
// b9 = 'abc' // error
/* 定義變量時(shí)沒(méi)有賦值, 推斷為any類型 */
letb10// any類型
b10=123
b10='abc'
接口
接口初探
需求: 創(chuàng)建人的對(duì)象, 需要對(duì)人的屬性進(jìn)行一定的約束
id是number類型, 必須有, 只讀的
name是string類型, 必須有
age是number類型, 必須有
sex是string類型, 可以沒(méi)有
下面通過(guò)一個(gè)簡(jiǎn)單示例來(lái)觀察接口是如何工作的:
/*
在 TypeScript 中酪惭,我們使用接口(Interfaces)來(lái)定義對(duì)象的類型
接口: 是對(duì)象的狀態(tài)(屬性)和行為(方法)的抽象(描述)
接口類型的對(duì)象
?? 多了或者少了屬性是不允許的
?? 可選屬性: ?
?? 只讀屬性: readonly
*/
/*
需求: 創(chuàng)建人的對(duì)象, 需要對(duì)人的屬性進(jìn)行一定的約束
? id是number類型, 必須有, 只讀的
? name是string類型, 必須有
? age是number類型, 必須有
? sex是string類型, 可以沒(méi)有
*/
// 定義人的接口
interfaceIPerson{
id:number
name:string
age:number
sex:string
}
constperson1:IPerson={
id:1,
name:'tom',
age:20,
sex:'男'
}
類型檢查器會(huì)查看對(duì)象內(nèi)部的屬性是否與IPerson接口描述一致, 如果不一致就會(huì)提示類型錯(cuò)誤希痴。
屬性
接口里的屬性不全都是必需的。 有些是只在某些條件下存在春感,或者根本不存在砌创。
interfaceIPerson{
id:number
name:string
age:number
sex?:string
}
帶有可選屬性的接口與普通的接口定義差不多虏缸,只是在可選屬性名字定義的后面加一個(gè) ? 符號(hào)。
可選屬性的好處之一是可以對(duì)可能存在的屬性進(jìn)行預(yù)定義嫩实,好處之二是可以捕獲引用了不存在的屬性時(shí)的錯(cuò)誤刽辙。
constperson2:IPerson={
id:1,
name:'tom',
age:20,
// sex: '男' // 可以沒(méi)有
}
只讀屬性
一些對(duì)象屬性只能在對(duì)象剛剛創(chuàng)建的時(shí)候修改其值。 你可以在屬性名前用 readonly 來(lái)指定只讀屬性:
interfaceIPerson{
readonlyid:number
name:string
age:number
sex?:string
}
一旦賦值后再也不能被改變了甲献。
constperson2:IPerson={
id:2,
name:'tom',
age:20,
// sex: '男' // 可以沒(méi)有
// xxx: 12 // error 沒(méi)有在接口中定義, 不能有
}
person2.id=2// error
readonly vs const
最簡(jiǎn)單判斷該用 readonly 還是 const 的方法是看要把它做為變量使用還是做為一個(gè)屬性宰缤。 做為變量使用的話用 const,若做為屬性則使用 readonly晃洒。
函數(shù)類型
接口能夠描述 JavaScript 中對(duì)象擁有的各種各樣的外形慨灭。 除了描述帶有屬性的普通對(duì)象外,接口也可以描述函數(shù)類型球及。
為了使用接口表示函數(shù)類型氧骤,我們需要給接口定義一個(gè)調(diào)用簽名。它就像是一個(gè)只有參數(shù)列表和返回值類型的函數(shù)定義吃引。參數(shù)列表里的每個(gè)參數(shù)都需要名字和類型筹陵。
/*
接口可以描述函數(shù)類型(參數(shù)的類型與返回的類型)
*/
interfaceSearchFunc{
(source:string,subString:string):boolean
//參數(shù)的類型? ? ? ? ? ? ? ? ? ? ? //返回的值
}
這樣定義后,我們可以像使用其它接口一樣使用這個(gè)函數(shù)類型的接口镊尺。 下例展示了如何創(chuàng)建一個(gè)函數(shù)類型的變量朦佩,并將一個(gè)同類型的函數(shù)賦值給這個(gè)變量。
constmySearch:SearchFunc=function(source:string,sub:string):boolean{
returnsource.search(sub)>-1
}
console.log(mySearch('abcd','bc'))
類類型
類實(shí)現(xiàn)接口
與 C# 或 Java 里接口的基本作用一樣庐氮,TypeScript 也能夠用它來(lái)明確的強(qiáng)制一個(gè)類去符合某種契約语稠。
/*
類類型: 實(shí)現(xiàn)接口
1. 一個(gè)類可以實(shí)現(xiàn)多個(gè)接口
2. 一個(gè)接口可以繼承多個(gè)接口
*/
interfaceAlarm{
alert():any;
}
interfaceLight{
lightOn():void;
lightOff():void;
}
classCarimplementsAlarm{
alert() {
console.log('Car alert');
? }
}
一個(gè)類可以實(shí)現(xiàn)多個(gè)接口
classCar2implementsAlarm,Light{
alert() {
console.log('Car alert');
? }
lightOn() {
console.log('Car light on');
? }
lightOff() {
console.log('Car light off');
? }
}
接口繼承接口
和類一樣,接口也可以相互繼承旭愧。 這讓我們能夠從一個(gè)接口里復(fù)制成員到另一個(gè)接口里颅筋,可以更靈活地將接口分割到可重用的模塊里。
interfaceLightableAlarmextendsAlarm,Light{
}
類
基本示例
下面看一個(gè)使用類的例子:
/*
類的基本定義與使用
*/
classGreeter{
// 聲明屬性
message:string
// 構(gòu)造方法
constructor(message:string) {
this.message=message
? }
// 一般方法
greet():string{
return'Hello '+this.message
? }
}
// 創(chuàng)建類的實(shí)例
constgreeter=newGreeter('world')
// 調(diào)用實(shí)例的方法
console.log(greeter.greet())
// 1.類 類型:類的類型输枯,類的類型议泵,類的類型可以通過(guò)接口來(lái)實(shí)現(xiàn)
(()=>{
// 定義一個(gè)接口
interfaceIFly{
fly()
? }
//定義一個(gè)類,這個(gè)類就是上面定義的接口桃熄,也可以理解為先口,IFly接口約束了當(dāng)前的這個(gè)Person類
classPersonimplementsIFly{//implement實(shí)現(xiàn)? person 實(shí)現(xiàn) IFly接口
//實(shí)現(xiàn)接口的方法
fly() {
console.log('我會(huì)飛了,我是超人')
? }
}
// 實(shí)例化對(duì)象
constperson=newPerson()
person.fly()
//--------------------------------
//2.定義一個(gè)接口
interfaceISwim{
swim()
}
// 定義一個(gè)類瞳收,這個(gè)類就是IFly和ISwim(當(dāng)前這個(gè)類可以實(shí)現(xiàn)多個(gè)接口碉京,一個(gè)類同時(shí)也可以被多個(gè)接口進(jìn)行約束)
classPerson2implementsIFly,ISwim{
fly(){
console.log('我飛了2')
? }
swim() {
console.log('我游泳2')
? }
}
//實(shí)例化對(duì)象
constperson2=newPerson2
person2.fly()
person2.swim()
// 類可以通過(guò)接口的方式來(lái)定義這個(gè)類的類型
// 類可以實(shí)現(xiàn)一個(gè)接口,也可以實(shí)現(xiàn)多個(gè)接口螟深。要注意接口中內(nèi)容都要真正的是實(shí)現(xiàn)
//--------------------------------
//3.定義了一個(gè)接口谐宙,可以繼承其他的多個(gè)接口
interfaceIMyFlyAndSwimextendsIFly,ISwim{? }
//定義一個(gè)類,直接實(shí)現(xiàn)IMFlyAndSwim這個(gè)接口
classPerson3implementsIMyFlyAndSwim{
fly(){
console.log('我飛了2')
? }
swim() {
console.log('我游泳2')
? }
}
})()
如果你使用過(guò) C# 或 Java界弧,你會(huì)對(duì)這種語(yǔ)法非常熟悉凡蜻。 我們聲明一個(gè) Greeter 類搭综。這個(gè)類有 3 個(gè)成員:一個(gè)叫做 message 的屬性,一個(gè)構(gòu)造函數(shù)和一個(gè) greet 方法划栓。
你會(huì)注意到兑巾,我們?cè)谝萌魏我粋€(gè)類成員的時(shí)候都用了 this。 它表示我們?cè)L問(wèn)的是類的成員忠荞。
后面一行蒋歌,我們使用 new 構(gòu)造了 Greeter 類的一個(gè)實(shí)例。它會(huì)調(diào)用之前定義的構(gòu)造函數(shù)委煤,創(chuàng)建一個(gè) Greeter 類型的新對(duì)象堂油,并執(zhí)行構(gòu)造函數(shù)初始化它。
最后一行通過(guò) greeter 對(duì)象調(diào)用其 greet 方法
類
//類:可以理解為模板碧绞,通過(guò)模板可以實(shí)例化對(duì)象
//面向?qū)ο蟮木幊趟枷?/p>
(() => {
?? // ts 中類的定義及使用
?? class Person {
? ? ?? // 定義屬性
? ? ?? name:string
? ? ?? age:number
? ? ?? gender:string
? ? ?? // 定義構(gòu)造函數(shù):為了將來(lái)實(shí)例化對(duì)象的時(shí)候称诗,可以直接對(duì)屬性的值進(jìn)行初始化
? ? ?? constructor(name:string='大傻春',age:number=18,gender:string='男'){
? ? ? ? ?? //更新對(duì)象中的屬性數(shù)據(jù)
? ? ? ? ?? this.name = name
? ? ? ? ?? this.age = age
? ? ? ? ?? this.gender = gender
? ? ?? }
? ? ?? // 定義實(shí)例方法
? ? ?? sayHi(str:string){
? ? ? ? ?? console.log(`大家好,我是${this.name},今年${this.age}头遭,性別${this.gender}`,str)
? ? ?? }
?? }
?? //ts中使用類,實(shí)例化對(duì)象癣诱,可以直接進(jìn)行初始化操作
?? const person = new Person('二傻',10,'女')
?? person.sayHi('你是誰(shuí)')
})()
繼承
// 繼承:類與類之間的關(guān)系
// 繼承后類與類之間的叫法:
// A類繼承了B這個(gè)類计维,那么A類叫做子類,B類叫做基類
// 子類:派生類
// 基類:超類(父類)
// 發(fā)生了繼承關(guān)系就出現(xiàn)父子關(guān)系
(() => {
?? // 定義一個(gè)類撕予,作為基類(超類/父類)
?? class Person {
? ? ?? name:string
? ? ?? age:number
? ? ?? gender:string
? ? ?? //定義構(gòu)造函數(shù)
? ? ?? constructor(name:string = '小明',age:number=18,gender:string = '男') {
? ? ? ? ?? //更新屬性數(shù)據(jù)
? ? ? ? ?? this.name = name
? ? ? ? ?? this.age = age
? ? ? ? ?? this.gender = gender
? ? ?? }
? ? ?? //定義實(shí)例方法
? ? ?? sayHi(str:string) {
? ? ? ? ?? console.log(`我是:${this.name},今年${this.age},${this.gender},${str}`)
? ? ?? }
?? }
?? // 定義一個(gè)類鲫惶,繼承自Person
?? class Student extends Person {
? ? ?? constructor(name:string,age:number,gender:string){
? ? ? ? ?? // 調(diào)用的父類的構(gòu)造函數(shù),使用的是super
? ? ? ? ?? super(name,age,gender)//super()訪問(wèn)和調(diào)用一個(gè)對(duì)象父對(duì)象上的函數(shù)
? ? ?? }
? ? ?? //可以調(diào)用父類中的方法
? ? ?? sayHi(){
? ? ? ? ?? console.log('學(xué)生類的sayHi方法')
? ? ? ? ?? super.sayHi('1')
? ? ?? }
?? }
?? // 實(shí)例化Person
?? const person = new Person()
?? person.sayHi('2')
?? //實(shí)例化Student
?? const stu = new Student('3',16,'女')
?? stu.sayHi()
?? // Student先使用extends關(guān)鍵字來(lái)繼承Person類实抡,然后實(shí)例化Student類stu欠母,然后stu方法使用父級(jí)方法sayHi
?? //子類調(diào)用父類的構(gòu)造函數(shù)使用的是super關(guān)鍵字
?? //子類可以重寫父類的方法
})()
多態(tài)
// 多態(tài): 父類型的引用指向了子類型的對(duì)象,不同類型的對(duì)象針對(duì)相同的方法吆寨,產(chǎn)生了不同的行為
(()=>{
// 定義父類
classAnimal{
name:string
//定義一個(gè)構(gòu)造函數(shù)
constructor(name:string){
// 更新數(shù)據(jù)值
this.name=name
? ? ?? }
// 實(shí)例方法
run(distance:number=1){//distance距離
console.log(this.name,`跑了${distance}米`)
? ? ?? }
?? }
//定義子類
classDogextendsAnimal{
//構(gòu)造函數(shù)
constructor(name:string){
//調(diào)用父類的構(gòu)造函數(shù)赏淌,實(shí)現(xiàn)子類中屬性的初始化
super(name)
? ? ?? }
//實(shí)例方法
run(distance:number=5){//distance距離
console.log(this.name,`跑了${distance}米`)
? ? ?? }
?? }
//定義子類
classPigextendsAnimal{
//構(gòu)造函數(shù)
constructor(name:string){
//調(diào)用父類的構(gòu)造函數(shù),實(shí)現(xiàn)子類中屬性的初始化
super(name)
? ? ?? }
//實(shí)例方法
run(distance:number=10){//distance距離
console.log(this.name,`跑了${distance}米`)
? ? ?? }
?? }
// 實(shí)例化父對(duì)象
constani:Animal=newAnimal('動(dòng)物')
ani.run()
//實(shí)例化子對(duì)象
constdog:Dog=newDog('大黃')
dog.run()
// 實(shí)例化子類對(duì)象
constpig:Pig=newPig('豬豬俠')
pig.run()
// 父類和子類的關(guān)系:父子關(guān)系啄清,此時(shí)六水,父類類型創(chuàng)建子類的對(duì)象
constdog1:Animal=newDog('小黃')
dog1.run()
constpig1:Animal=newPig('小豬豬俠')
pig1.run()
// 該函數(shù)需要的參數(shù)是Animal類型的
functionshowRun(ani:Animal) {
ani.run()
showRun(dog1)
showRun(pig1)
?? }
})()
// 修飾符(類中成員的修飾符):主要是描述類中的成員(屬性,構(gòu)造函數(shù)辣卒,方法)的可訪問(wèn)性
// 類中的成員都有自己的默認(rèn)訪問(wèn)符掷贾,public
// public修飾符---公共的,類中的成員默認(rèn)的修飾符荣茫,代表的是公共的想帅。任何位置都可以訪問(wèn)類中的成員
// private修飾符--- 私有的,類中的成員如果使用private來(lái)修飾啡莉,那么外部是無(wú)法訪問(wèn)這個(gè)成員數(shù)據(jù)的港准,當(dāng)然旨剥,子類也無(wú)法訪問(wèn)該成員數(shù)據(jù)
// protected修飾符,類中的成員使用protected來(lái)修飾叉趣,那么外部無(wú)法訪問(wèn)這個(gè)成員的數(shù)據(jù)泞边,子類卻可以訪問(wèn)。
(()=>{
//定義一個(gè)類
classPerson{
// public name:string
// private name:string
protectedname:string
?? }
})()
讀取器 get set
(()=>{
classPerson{
firstName:string
lastName:string
constructor(firstName:string,lastName:string){
this.firstName=firstName
this.lastName=lastName
? ? ?? }
//讀取器
getfullName() {
console.log
returnthis.firstName+'_'+this.fullName
? ? ?? }
//修改器
setfullName(aaa) {
letnames=aaa.split('_')
this.firstName=names[0]
this.lastName=names[1]
? ? ?? }
?? }
constperson:Person=newPerson('西門','吹雪')
console.log(person)
person.fullName='南宮_問(wèn)天'
console.log(person.fullName)
})()
靜態(tài)成員(靜態(tài)屬性/靜態(tài)方法)
// 靜態(tài)成員: 在類中通過(guò)static修飾的屬性或者方法疗杉,那么就是靜態(tài)的屬性及靜態(tài)的方法阵谚,也稱為靜態(tài)成員
// 靜態(tài)成員在使用的時(shí)候是通過(guò)類名.的語(yǔ)法來(lái)調(diào)用的
(()=>{
classPerson{
// 靜態(tài)屬性
// 類中默認(rèn)有一個(gè)內(nèi)置的name屬性
staticname1:string='123'
// 構(gòu)造函數(shù)是不能通過(guò)static來(lái)進(jìn)行修飾的
//static constructor(){} error
// 靜態(tài)屬性
staticsayHi(){
console.log('321')
? ? ?? }
?? }
//const person:Person =new Person('456')
// 通過(guò)類名.靜態(tài)方法來(lái)調(diào)用內(nèi)部的靜態(tài)方法
Person.sayHi()
// 通過(guò)類名.靜態(tài)屬性來(lái)訪問(wèn)成員數(shù)據(jù)
console.log(Person.name1)
// 通過(guò)類名. 靜態(tài)方法的方式來(lái)設(shè)置成員數(shù)據(jù)
Person.name1='999'
console.log(Person.name1)
})()
抽象類
// 抽象類:包含抽象方法(抽象方法一般沒(méi)有任何的具體內(nèi)容的實(shí)現(xiàn)),
//也可以包含實(shí)例方法烟具,抽象類是不能被實(shí)例化的梢什,為了讓子類進(jìn)行實(shí)例化及實(shí)現(xiàn)內(nèi)部的抽象方法
(()=>{
// 定義一個(gè)抽象類
abstractclassAnimal{
//抽象方法
abstracteat()// 抽象方法不能有具體的實(shí)現(xiàn)
//實(shí)例方法
sayHi(){
console.log('吃')
? ? ? }
? }
// 抽象類的對(duì)象是無(wú)法實(shí)例化的
//const ani:Animal = new Animal ? /error
//定義一個(gè)子類()派生類
classDogextendsAnimal{
eat(){
console.log('飛著吃')
? ?? }
? }
})()
// 總結(jié)感受,有點(diǎn)像接口朝聋,先把子類的各項(xiàng)類似的方法抽象出來(lái)嗡午,在父類中抽象方法
函數(shù)
// 函數(shù):一些被頻繁使用的代碼封裝起來(lái)。在需要的時(shí)候直接調(diào)用即可
(()=>{
// // js寫法
// // 函數(shù)聲明冀痕,命名函數(shù)
// function add(x,y){
// ? ? return x + y
// }
// //函數(shù)表達(dá)式荔睹,匿名函數(shù)
// const add2 = function (x,y){
// ? ? return x + y
// }
//-----------------------------------
// ts寫法
// 函數(shù)聲明,命名函數(shù)
functionadd(x:string,y:string):string{
//括弧內(nèi)參數(shù)的:string指的是傳入的x是string類型括弧外的:string是指函數(shù)的返回值是string
returnx+y
?? }
//函數(shù)表達(dá)式言蛇,匿名函數(shù)
constadd2=function(x:number,y:number):number{
returnx+y
?? }
//ts函數(shù)的完整寫法
//函數(shù)本身類型中的 => 和 es6 沒(méi)關(guān)系僻他,是 ts 中關(guān)于定義函數(shù)本身類型的語(yǔ)法
//add3--->變量名--->函數(shù)add3
//(x:number,y:number) => number 當(dāng)前這個(gè)函數(shù)的類型 =>在這里不是箭頭函數(shù),是定義函數(shù)的類型
//function (x:number,y:number):number {return x+y} 就相當(dāng)于符合上面函數(shù)的值
constadd3:(x:number,y:number)=>number=function(x:number,y:number) :number{
returnx+y
?? }
constas=add3(10,20)
console.log(as)
})()
可選?和默認(rèn)參數(shù)
// 可選參數(shù):函數(shù)在聲明的時(shí)候腊尚,內(nèi)部的參數(shù)使用了吨拗?進(jìn)行了修飾,那么就表示該參數(shù)可以傳入也可以不傳入
// 默認(rèn)參數(shù):函數(shù)在聲明的時(shí)候內(nèi)部的參數(shù)有自己的默認(rèn)值婿斥,此時(shí)的這個(gè)參數(shù)就可以叫做默認(rèn)參數(shù)劝篷。
(()=>{
constgetFullName=function(firstName:string='東方',lastName?:string):string{
if(lastName){
returnfirstName+'_'+lastName
}else{
returnfirstName
? ? ?? }
?? }
// 函數(shù)調(diào)用
// 什么也不傳入
console.log(getFullName())
// 只傳入姓氏
console.log(getFullName('諸葛'))
//傳入姓氏和名字
console.log(getFullName('諸葛','亮'))
})()
剩余參數(shù)
// 剩余參數(shù)(rest參數(shù))
// 剩余參數(shù)是放在函數(shù)聲明的時(shí)候所有的參數(shù)的最后
(()=>{
// ...args:string[]---->剩余的參數(shù),放在了一個(gè)字符串的數(shù)組中民宿,args里面
functionshowMsg(str:string,...args:string[]){
console.log(str)//a
console.log(args)//b c d e
?? }
showMsg('a','b','c','b')
})()
函數(shù)重載
// 函數(shù)重載:函數(shù)的名字不同娇妓,函數(shù)的參數(shù)及個(gè)數(shù)不同
(()=>{
// 定義一個(gè)函數(shù)
//函數(shù)重載聲明
functionadd(x:string,y:string):string
functionadd(x:number,y:number):number
// 函數(shù)聲明
functionadd(x:string|number,y:string|number):string|number{
if(typeofx==='string'&&typeofy==='string') {
returnx+y
}elseif(typeofx==='number'&&typeofy==='number') {
returnx+y
? ? ?? }
?? }
// 函數(shù)調(diào)用
console.log(add('諸葛','孔明'))
// 兩個(gè)參數(shù)都是數(shù)字
console.log(add(10,20))
//函數(shù)重載應(yīng)用, 傳入的是非法的數(shù)據(jù)活鹰,ts給出提出錯(cuò)誤信息的內(nèi)容峡蟋,報(bào)紅色。
console.log(add('諸葛',12))
console.log(add(23,'孔明'))
})()
泛型
泛型
// 泛型:在定義函數(shù)华望、接口蕊蝗、類的時(shí)候、不能預(yù)先確定要使用的數(shù)據(jù)的類型赖舟,而是在使用函數(shù)蓬戚、接口、類的時(shí)候才能確定數(shù)據(jù)的類型
// 使用方法:<>
(()=>{
?? function getArr4<T>(value:T,count:number):T[]{
? ? ?? // <T> T 類似形參宾抓,根據(jù)需要定
? ? ?? const arr :Array<T> = []
? ? ?? for (let i= 0;i< count;i++){
? ? ? ? ?? arr.push(value)
? ? ?? }
? ? ?? return arr
?? }
?? const arr1 = getArr4<number>(200.1234,5)
?? //在使用的使用子漩,<>里面的規(guī)定數(shù)據(jù)類型
?? const arr2 = getArr4<string>('abcdefg',5)
?? console.log(arr1)
?? console.log(arr1[0].toFixed(3))
?? console.log(arr2[0].split(''))
})()
多個(gè)泛型參數(shù)
(()=>{
?? // 多個(gè)泛型參數(shù)的函數(shù):函數(shù)中有多個(gè)泛型的參數(shù)
?? function getMsg<K,V>(value1:K,value2:V):[K,V] {
? ? ?? return [value1,value2]
?? }
?? const arr1 = getMsg<string,number>('jack',100.2345)
?? console.log(arr1[0].split(''))
?? console.log(arr1[1].toFixed(1))
})()
泛型接口
//泛型接口:在定義接口時(shí)豫喧,為接口中的屬性或者方法定義泛型類型,在使用接口的時(shí)候再具體指定具體的泛型接口
(()=>{
// 定義一個(gè)泛型的接口
interfaceIBaseCRUD<T>{
data:Array<T>
add: (t:T)=>T
getUserId: (id:number)=>T
?? }
// 定義了用戶信息的類
classUser{
id?:number
name:string
age:number
constructor(name:string,age:number) {
this.name=name
this.age=age
? ? ?? }
?? }
//再定義一個(gè)增刪改查操作的類
//CRUD----create,read,update,delete
classUserCRUDimplementsIBaseCRUD<User>{
data:Array<User>=[]
add(user:User):User{
user.id=Date.now()+Math.random()
this.data.push(user)
returnuser
}//儲(chǔ)存用戶信息
getUserId(id:number):User{
returnthis.data.find(user=>user.id===id)
? ? ?? }
//根據(jù)id查找用戶信息對(duì)象
?? }
//實(shí)例化添加用戶信息對(duì)象的類UserCRUD
constuserCRUD=newUserCRUD()
userCRUD.add(newUser('jack',20))
userCRUD.add(newUser('bob',18))
const{id}=userCRUD.add(newUser('tom',22))
constuser=userCRUD.getUserId(id)
console.log(userCRUD.data)
console.log(user)
})()
泛型類
(()=>{
// 定義一個(gè)類幢泼,類中的屬性值的類型是不確定的紧显,方法中的參數(shù)和返回值的類型也是不確定
// 定義一個(gè)泛型類
classGenericNumber<T>{
//默認(rèn)的屬性的值是泛型類型
defaultValue:T
//函數(shù)本身類型中的 => 和 es6 沒(méi)關(guān)系,是 ts 中關(guān)于定義函數(shù)本身類型的語(yǔ)法
add:(x:T,y:T)=>T
?? }
//在實(shí)例化類的對(duì)象時(shí)候缕棵,再確定泛型的類型
constg1:GenericNumber<number>=newGenericNumber<number>()
// 設(shè)置屬性值
g1.defaultValue=100
// 相加的方法
g1.add=function(x,y) {
returnx+y
?? }
})()
泛型約束
(()=>{
?? //如果直接對(duì)一個(gè)泛型參數(shù)取length屬性孵班,會(huì)error,因?yàn)榉盒筒恢浪羞@個(gè)屬性
?? // 定義一個(gè)接口招驴,用于約束將來(lái)的莫個(gè)類型中必需的length屬性
?? interface ILength{
? ? ?? // 接口中有一個(gè)屬性:length
? ? ?? length:number
?? }
?? function getLength<T extends ILength>(x:T):number{
? ? ?? return x.length
?? }
?? console.log(getLength<string>('afsdfdsafsad'))
?? console.log(getLength<number>(1234))// 因?yàn)閚umber沒(méi)有l(wèi)ength篙程,所有不滿足約束,error
})()
其他
當(dāng)使用第三方庫(kù)時(shí)别厘,我們需要引用它的聲明文件虱饿,才能獲得對(duì)應(yīng)的代碼補(bǔ)全、接口提示等功能
很多的第三方庫(kù)都定義了對(duì)應(yīng)的聲明文件庫(kù), 庫(kù)文件名一般為 @types/xxx, 可以在
https://www.npmjs.com/package/package 進(jìn)行搜索
有的第三庫(kù)在下載時(shí)就會(huì)自動(dòng)下載對(duì)應(yīng)的聲明文件庫(kù)(比如: webpack),有的可能需要單獨(dú)下載(比如jQuery/react)
例如:下載jQuery聲明文件: npm install @types/jquery --save-dev
什么是聲明語(yǔ)句
假如我們想使用第三方庫(kù) jQuery触趴,一種常見的方式是在 html 中通過(guò) ` 標(biāo)簽引入jQuery氮发,然后就可以使用全局變量$或jQuery` 了。
但是在 ts 中冗懦,編譯器并不知道 $ 或 jQuery 是什么東西
/*
當(dāng)使用第三方庫(kù)時(shí)折柠,我們需要引用它的聲明文件,才能獲得對(duì)應(yīng)的代碼補(bǔ)全批狐、接口提示等功能。
聲明語(yǔ)句: 如果需要ts對(duì)新的語(yǔ)法進(jìn)行檢查, 需要要加載了對(duì)應(yīng)的類型說(shuō)明代碼
? declare var jQuery: (selector: string) => any;
聲明文件: 把聲明語(yǔ)句放到一個(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 來(lái)定義它的類型
declarevarjQuery: (selector:string)=>any;
jQuery('#foo');
declare var 并沒(méi)有真的定義一個(gè)變量嚣艇,只是定義了全局變量 jQuery 的類型,僅僅會(huì)用于編譯時(shí)的檢查华弓,在編譯結(jié)果中會(huì)被刪除食零。它編譯結(jié)果是:
jQuery('#foo');
一般聲明文件都會(huì)單獨(dú)寫成一個(gè) xxx.d.ts 文件
創(chuàng)建 01_jQuery.d.ts, 將聲明語(yǔ)句定義其中, TS編譯器會(huì)掃描并加載項(xiàng)目中所有的TS聲明文件
declarevarjQuery: (selector:string)=>any;
內(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ì)象
BooleanNumberStringDateRegExpError
/* 1. ECMAScript 的內(nèi)置對(duì)象 */
letb:Boolean=newBoolean(1)
letn:Number=newNumber(true)
lets:String=newString('abc')
letd:Date=newDate()
letr:RegExp=/^1/
lete:Error=newError('error message')
b=true
// let bb: boolean = new Boolean(2)? // error
BOM 和 DOM 的內(nèi)置對(duì)象
WindowDocumentHTMLElementDocumentFragmentEventNodeList
constdiv:HTMLElement=document.getElementById('test')
constdivs:NodeList=document.querySelectorAll('div')
document.addEventListener('click', (event:MouseEvent)=>{
console.dir(event.target)
})
constfragment:DocumentFragment=document.createDocumentFragment()