TypeScript
TypeScript官網(wǎng) https://www.typescriptlang.org/zh/
TypeScript中文官網(wǎng) https://www.tslang.cn/samples/index.html
編程語言的類型分為動態(tài)類型語言(Dynamically Typed Language)和靜態(tài)類型語言(Statically Typed Language)浦译。
動態(tài)類型語言
是在運行期間才會進行類型檢查的語言衔峰。類型對于變量,屬性,方法以及方法的返回類型都是可有可無的屡谐,在給變量賦值時才決定它的類型,之后還可以賦值不同類型的值疾棵,即使是基本類型黎侈。例如JavaScript,python涝桅,Ruby拜姿。
靜態(tài)類型語言
是在編譯階段進行類型檢查。寫程序時要聲明變量的數(shù)據(jù)類型冯遂。例如C蕊肥,C++,Java债蜜。
TypeScript
就是把不看重類型的動態(tài)語言變成關(guān)注類型的動態(tài)語言晴埂。是Javascript的超集,提供從es6到es10寻定,甚至是esnext的語法支持儒洛。兼容各種瀏覽器、系統(tǒng)狼速,完全開源琅锻。
為什么使用TypeScript?
- 程序更容易理解
對于確定函數(shù)或者方法輸入輸出的參數(shù)類型向胡,外部條件等恼蓬,動態(tài)類型語言需要手動調(diào)試等過程,例如debugger僵芹、console处硬。有了TypeScript就很容易確定以上內(nèi)容。 - 效率更高
在不同的代碼塊和定義中跳轉(zhuǎn)拇派,代碼自動補全荷辕。 - 更少的錯誤
編譯期間就能夠發(fā)現(xiàn)大部分錯誤,避免一些常見錯誤(property 'xxxx' undefined) - 非常好的包容性
完全兼容JavaScript件豌,第三方庫可以單獨編寫類型文件疮方。
但會增加一定程度上的學(xué)習(xí)成本,短期內(nèi)增加了一些開發(fā)成本茧彤。所以TS適用于一些中大型長期開發(fā)維護的項目骡显。
TypeScript安裝
安裝node,或確定已安裝的node的版本至少為10.0.0以上。
全局安裝TypeScript惫谤,運行npm install -g typescript壁顶。安裝完成之后輸入tsc -v可查看版本號。(tsc 代表 TypeScript 編譯器溜歪,只要編譯器運行博助,它將在項目文件夾中查找名為tsconfig.json 的文件。)
基礎(chǔ)類型
原始數(shù)據(jù)類型(primitive values):
Boolean痹愚、Null富岳、Undefined、Number拯腮、BigInt(es6新增)窖式、String、Symbo
let isDone:boolean = false
let age:number = 10
let firstName:string = 'vivid'
let message:string = `Hello,${firstName}`
let u:undefined = undefined
let n:null = null
//undefined和null是所有類型的子類型
let num:number = undefined
注:null and undefined
null表示"沒有對象"动壤,即該處不應(yīng)該有值萝喘,轉(zhuǎn)為數(shù)值時為0。 在內(nèi)存里的表示就是琼懊,棧中的變量沒有指向堆中的內(nèi)存對象阁簸。
1、作為函數(shù)的參數(shù)哼丈,表示該函數(shù)的參數(shù)不是對象
2启妹、作為對象原型鏈的終點
undefined表示"缺少值",就是此處應(yīng)該有一個值醉旦,但是還沒有定義饶米,轉(zhuǎn)為數(shù)值時為NaN。
1车胡、變量被聲明了檬输,但沒有賦值時,就等于undefined
2匈棘、調(diào)用函數(shù)時丧慈,應(yīng)該提供的參數(shù)沒有提供,該參數(shù)等于undefined
3主卫、對象沒有賦值的屬性逃默,該屬性的值為undefined
4、函數(shù)沒有返回值時队秩,默認(rèn)返回undefined
console.log(typeof (null))// object
console.log(typeof (undefined))// undefined
Number(null)//0
Number(undefined)// NaN
null == undefined// true
null === undefined// false
//變量被聲明笑旺,但沒有賦值
var a
console.log(a)// undefined
//函數(shù)定義了形參昼浦,但沒有傳遞實參
function fn(a) {
console.log(a)// undefined
}
fn()
//引用對象中不存在的屬性
console.log(Object.a); // undefined
//函數(shù)沒有返回值
var a = fn()
console.log(a)// undefined
Any和unkonw
有時候馍资,我們會想要為那些在編程階段還不清楚類型的變量指定一個類型。 這些值可能來自于動態(tài)的內(nèi)容,比如來自用戶輸入或第三方代碼庫鸟蟹。 這種情況下乌妙,我們不希望類型檢查器對這些值進行檢查而是直接讓它們通過編譯階段的檢查。 那么我們可以使用 any
類型來標(biāo)記這些變量:
let notSure:any = 4
notSure = 'maybe a string'
notSure = true
notSure = {Hello: () => {}}
譯者: any 和 unknown 的最大區(qū)別是, unknown 是 top type (任何類型都是它的 subtype) , 而 any 即是 top type, 又是 bottom type (它是任何類型的 subtype ) , 這導(dǎo)致 any 基本上就是放棄了任何類型檢查建钥。
對照于 any藤韵,unknown 是類型安全的。 任何值都可以賦給 unknown熊经,但是當(dāng)沒有類型斷言或基于控制流的類型細(xì)化時 unknown 不可以賦值給其它類型泽艘,除了它自己和 any 外。 同樣地镐依,在 unknown 沒有被斷言或細(xì)化到一個確切類型之前匹涮,是不允許在其上進行任何操作的。
//任何值都可以賦給 unknown
let uncertain:unknown = 5
uncertain = 'is a string'
uncertain = {Hello: () => {}}
// unknown 不可以賦值給其它類型槐壳,除了它自己和 any 外
notSure = uncertain
let isDone:boolean = false
isDone = uncertain//Type 'unknown' is not assignable to type 'boolean'.
//簡單應(yīng)用
const isFalsy = (value: unknown) => (value === 0 ? false : !value);
console.log(isFalsy(''))//true
Array和Tuple
Array 有兩種方式可以定義數(shù)組然低。 第一種,可以在元素類型后面接上 []务唐,表示由此類型元素組成的一個數(shù)組:
//數(shù)組的項不允許出現(xiàn)其他類型
let arrOfNumber: number[] = [1,2,3]
//數(shù)組的一些方法的參數(shù)也會根據(jù)數(shù)組在定義時約定的類型進行限制:
arrOfNumbers.push(3)
arrOfNumbers.push('abc')
第二種方式是使用數(shù)組泛型雳攘,Array<元素類型>:
let arrOfString: Array<String> = ['abc','def','g']
Tuple(元組)
元組類型允許表示一個已知元素數(shù)量和類型的數(shù)組,各元素的類型不必相同枫笛。 其本質(zhì)還是一個數(shù)組吨灭,可以使用一些數(shù)組的方法。
let user:[string,number] = ['jack',29]
//數(shù)組中添加的項只能是定義的類型
user.push('man')
user.push(true)//Error, 布爾不是(string | number)類型
Interfaces 接口
對對象的類型進行描述刑巧,也被成為“鴨子類型”沃于。只在ts中作靜態(tài)檢查。
interface Person{
name: string,
age: Number
}
let rose: Person = {
name: 'rose',
age: 20
}
function userUser(infor:Person){
console.log('user')
}
userUser({name: 'rose',age: 20})
可選屬性:接口里的屬性不全都是必需的海诲。 有些是只在某些條件下存在繁莹,或者根本不存在。
//age是可選或者不選的
interface Person{
name: string,
age?: Number
}
let rose: Person = {
name: 'rose',
}
只讀屬性:一些對象屬性只能在對象剛剛創(chuàng)建的時候修改其值特幔。 你可以在屬性名前用 readonly來指定只讀屬性咨演。
interface Person{
readonly id: number,
name: string,
age?: Number
}
let rose: Person = {
id: 1,
name: 'rose',
}
rose.id = 2//Error
//TypeScript具有ReadonlyArray<T>類型,它與Array<T>相似
let readonlyArr: ReadonlyArray<Number> = [1,2,3,4]
readonlyArr[0] = 6//Error,Index signature in type 'readonly Number[]' only permits reading
readonly vs const
最簡單判斷該用readonly還是const的方法是看要把它做為變量使用還是做為一個屬性蚯斯。 做為變量使用的話用 const薄风,若做為屬性則使用readonly。
Function 函數(shù)
我們可以給每個參數(shù)添加類型之后再為函數(shù)本身添加返回值類型拍嵌。 TypeScript能夠根據(jù)返回語句自動推斷出返回值類型遭赂,因此我們通常省略它。
//z為可選參數(shù)横辆,可選參數(shù)后面不能添加確定參數(shù)
function add(x:number,y:number,z?:number):number{
if (typeof z === 'number') {
return x+y+z
}else{
return x+y
}
}
add(1,2)
add(1,2,3)
//let add = (x: number, y: number): number => { return x + y; };
let add2: (x:number,y:number,z?:number) => number = add// ts中冒號后面都是在聲明類型
//另一種寫法
interface sum {
(x:number,y:number,z?:number): number
}
let add3:sum = add
Union Types 聯(lián)合類型撇他、Type assertions 類型斷言
//我們只需要用中豎線來分割兩個
let utype: number | string
//這里我們可以用 as 關(guān)鍵字,告訴typescript 編譯器,
//你沒法判斷我的代碼困肩,但是我本人很清楚划纽,這里我就把它看作是一個 string,你可以給他用 string 的方法锌畸。
function getLength(input:string|number):number{
const str = input as string
if (str.length){
return str.length
}else{
const number = input as number
return number.toString().length
}
}
//type guard類型守衛(wèi)
function getLength2(input:string|number):number{
if (typeof input === 'string'){
return input.length
}else{
return input.toString().length
}
}
Class 類
- public 修飾的屬性或方法是公有的勇劣,可以在任何地方被訪問到,默認(rèn)所有的屬性和方法都是 public 的
- private 修飾的屬性或方法是私有的潭枣,不能在聲明它的類的外部訪問
- protected 修飾的屬性或方法是受保護的比默,它和 private 類似,區(qū)別是它在子類中也是允許被訪問的
class Animal {
name: string;
constructor(name){
this.name = name
}
run(){
return `${this.name} is running`
}
private eat(){
return `${this.name} is eating`
}
}
const snake = new Animal('snake')
console.log(snake.run())
console.log(snake.eat())// Error
class Dog extends Animal{
dark(){
return `${this.name} is barking`
}
}
const xiaobei = new Dog('xiaobei')
console.log(xiaobei.run())
console.log(xiaobei.dark())
class Cat extends Animal{
static categories = ['mammal']
constructor(name){
super(name)
console.log(this.name)
}
run(){
return 'Meow,'+super.run()
}
}
const maomao = new Cat('maomao')
console.log(maomao.run())
console.log(Cat.categories)
Class 類和Interface接口
同類之間有共同的特性盆犁,可以把這些特性提取為接口退敦,可以使用implement來實現(xiàn)接口。
//類和接口
// class Car {
// switchRadio(trigger:boolean){}
// }
// class CellPhone {
// switchRadio(trigger:boolean){}
// }
interface Radio {
switchRadio(trigger:boolean): void;
}
interface Barrery {
checkBarrery(trigger:boolean): void;
}
interface RadioBattery extends Barrery {
checkBarrery(trigger:boolean): void;
}
class Car implements Radio{
switchRadio(trigger:boolean){}
}
class CellPhone implements Radio,Barrery{
switchRadio(trigger:boolean){}
checkBarrery(trigger:boolean){}
}
Enum 枚舉
一般會使用const來定義一些常量蚣抗,但有些取值是再范圍內(nèi)的一系列常量侈百,例如紅黃藍(lán)、上下左右翰铡,這時候就可以用枚舉來定義钝域。
// 數(shù)字枚舉
// 沒有定義初值時,初始值為0锭魔,其余的成員會從 0開始自動增長
enum Direction {
Up,
Down,
Left,
Right
}
console.log(Direction.Up);// 0
console.log(Direction[0]);// Up
//若定義一個數(shù)字枚舉例证,Up 的初始值為10,自動增長Down為11迷捧,Left為12织咧,Right為13
enum Direction {
Up = 10,
Down,
Left,
Right
}
console.log(Direction.Up);// 10
console.log(Direction[11]);// Down
// 字符串枚舉
// 在一個字符串枚舉里,每個成員都必須用字符串字面量漠秋,或另外一個字符串枚舉成員進行初始化笙蒙。字符串枚舉沒有自增長的行為。
enum Direction {
Up = 'Up',
Down = 'Down',
Left = 'Left',
Right = 'Right'
}
// 常量枚舉
// 為了避免在額外生成的代碼上的開銷和額外的非直接的對枚舉成員的訪問庆锦,我們可以使用 const枚舉
const enum Direction {
Up = 'Up',
Down = 'Down',
Left = 'Left',
Right = 'Right'
}
Generics 泛型
function echo(arg:any):any{
return arg
}
const result = echo(123)
上面的例子使用any類型會導(dǎo)致這個函數(shù)可以接收任何類型的arg參數(shù)捅位,這樣就丟失了一些信息:傳入的類型與返回的類型應(yīng)該是相同的。如果我們傳入一個數(shù)字搂抒,我們只知道任何類型的值都有可能被返回艇搀。
泛型在定義函數(shù)類的時候,可以不先指定類型求晶,而是可以在使用的時候指定類型焰雕。
//給函數(shù)echo類型變量T。 T幫助我們捕獲用戶傳入的類型(比如:number)
function echo<T>(arg: T): T{
return arg;
}
const result = echo('123')
function swap <T,U> (tuple:[T,U]): [U,T]{
return [tuple[1],tuple[0]]
}
const res = swap([1,'123'])
泛型約束
function echoWithArray<T>(arg:T):T{
console.log(arg.length) // Error: T doesn't have .length
return arg
}
相比于操作any所有類型芳杏,我們想要限制函數(shù)去處理任意帶有.length屬性的所有類型矩屁。 只要傳入的類型有這個屬性辟宗,我們就允許,就是說至少包含這一屬性档插。 為此,我們需要列出對于T的約束要求亚再。
function echoWithArray<T>(arg:T[]):T[]{
return arg
}
//更好的解決方法
function echoWithArray<T extends IWithLength>(arg:T):T{
console.log(arg.length)
return arg
}
interface IWithLength{
length: number
}
const res2 = echoWithArray([1,2,3])
const res3 = echoWithArray({length: 10})
泛型在類和接口中的使用
class Queue<T> {
private data = [];
push(item:T){
return this.data.push(item);
}
pop():T{
return this.data.shift();
}
}
const queue = new Queue<number>();
queue.push(1)
console.log(queue.pop().toFixed())
interface keyPair<T,U>{
key: T,
value: U
}
let kp1: keyPair<number,string> = {key: 1,value: '1'}
type alias類型別名
類型別名(type aliases)為一個類型創(chuàng)建一個新的名字郭膛。有些時候類型別名與接口(interfaces)很相似,但是類型別名可以用于聲明原始類型(primitives)氛悬,聯(lián)合類型(unions)则剃,元組類型(tuples),還有其它一些你必須要手寫的類型如捅。
type alias與interface的區(qū)別
// 1棍现、聲明對象和函數(shù)類型的語法不一樣
interface A {
x: number;
y: string;
}
interface B {
(m:number,n:string): void
}
type A = {
x: number;
y: string;
}
type B = (m:number,n:string): => void
//2、類型別名(type alias)可以用來聲明一些接口(interface)無法聲明的其他類型
// 聲明已有類型(即取別名)
type A = number;
// 字面量類型
type B = 'foo';
// 元組類型
type C = [number, string];
// 聯(lián)合類型
type D = number | boolean;
type Directions = 'UP' | 'DOWN' | 'LEFT' | 'RIGHT'
const toWhere:Directions = 'DOWN'
// 交叉類型
type E = A & D;
interface IName {
name: string
}
type IPerson = IName & {age:number}
const amy: IPerson = {name: 'amy',age: 20}
// 3镜遣、接口(interface)可以通過合并不同的聲明進行不斷擴展
interface A {
x: number;
}
interface A {
y: string;
}
// 經(jīng)過上面兩次聲明后 A 的類型為: { x: number; y: number; }
// 4己肮、接口(interface)通過 extends 關(guān)鍵字來擴展一個類型(這個類型可以是 interface, 也可以是 type alias),類型別名(type alias)則通過交叉類型來實現(xiàn)擴展
interface Super {
a: string;
}
interface Sub extends Super {
b: number;
}
// type alias
type Super = {
a: string;
};
type Sub = Super & {
b: number
};
聲明文件
在引用第三方庫的時候悲关,需要聲明文件谎僻。在xxx.d.ts文件中聲明
declare var JQuery: (selector: string) => any
//ts中的JQuery('#foo')就不會報錯
但我們不可能一個個去定義,所以在TypeScript 2.0以上的版本寓辱,獲取類型聲明文件只需要使用npm艘绍。例如獲取JQuery的聲明文件
npm install --save @types/jquery
TypeScript查詢對應(yīng)包的類型文件可在https://microsoft.github.io/TypeSearch/查詢。有些在安裝的時候秫筏,直接提供定義文件和源代碼诱鞠,安裝包里有xxx.d.ts就說明有定義文件。
內(nèi)置對象
根據(jù)標(biāo)準(zhǔn)在全局作用域(global)上存在的對象这敬,這里的標(biāo)準(zhǔn)是指ECMAScript和其他的環(huán)境(eg:DOM的標(biāo)準(zhǔn))航夺。在每個ts項目中會自動的加載這些對象。
//ECMAScript標(biāo)準(zhǔn)提供的內(nèi)置對象
const date = new Date();
date.getDate();
const reg = /[a-z]/
reg.test('ab')
// build-in Object
Math.random()
// DOM and BOM崔涂,提供的內(nèi)置對象有Document敷存、HTMLElement、Event堪伍、NodeList 等
let body: HTMLElement = document.body;
let allLis:NodeListOf<HTMLLIElement> = document.querySelectorAll('li');
allLis.keys();
document.addEventListener('click',(e)=>{
e.preventDefault();
})
Utility Types
Utility Types是ts內(nèi)置的實用類型锚烦,用于類型轉(zhuǎn)換。常用的類型有:Partial帝雇, Required涮俄, Readonly, Record<K,T>尸闸,Pick<T,K>彻亲,Omit<T,K>孕锄,Exclude<T,U>等。
文檔鏈接:https://www.typescriptlang.org/docs/handbook/utility-types.html
//Partial<T>
//將泛型傳入的T中所有屬性轉(zhuǎn)換為可選屬性苞尝,返回的類型可以是T的任意子集畸肆。
interface UPerson {
name: string,
age: number,
sex: boolean
}
const vivi = {name: 'vivi',age: 22,sex:true}
//相當(dāng)于type IPartial = {name?: string; age?: number;sex?:boolean}
type IPartial = Partial<UPerson>
const vivi2:IPartial = {name: 'vivi'}
//Required<T>
//將泛型傳入的T中所有屬性轉(zhuǎn)換為必須屬性,和Partial類型相反宙址。
type IRequired = Required<UPerson>
const vivi3:IRequired = {age: 20}// Error. Property 'name' is missing in type '{ age: number; }' but required in type 'Required<UPerson>'
//Pick<T,K>
//通過傳入的泛型T中選擇一組屬性K(字符串字面值或字符串字面值的聯(lián)合)來構(gòu)造類型轴脐。
//相當(dāng)于type IPick = {name: string;sex: boolean;}
type IPick = Pick<UPerson,'name' | 'sex'>
const vivi4:IPick = {name: 'vivi',sex: true}
//Omit<T,K>
//通過傳入的泛型T中選擇一組屬性K并刪除其他屬性,和Pick相反抡砂。
type Iomit = Omit<UPerson,'name'>
const vivi5:IPartial = {age: 20,sex: true}