初探 TypeScript

前段時間有朋友和我推薦 TypeScript ,他說寫起來特別爽,讓我去試一試,那時候我還在那是啥高深莫測的東西。恰好那段時間忙,一直沒有時間看。最近也很忙,仍是抽時間來探一探 TypeScript ;簡單說 ts 主要提供的是 dynamic type check,提供的 interface 接口這個功能在開發項目的時候會頗有幫助。TypeScriptJavaScript 的一個超集。他和 JavaScript 有着千絲萬縷的關係。前端

TypeScript 相關命令java

tsc -init     //ts文件編譯成js文件
tsc -w    //實時監控文件編譯,即一遍寫ts代碼保存的時候就能夠看到js代碼
複製代碼

運行了 tsc -init 之後會生成一個 tsconfig.json 配置文件web

敬畏每一行代碼,敬畏每一分託付

函數

函數是 JavaScript 裏面最基本的單位,我首先從函數入手慢慢的去學習更多的 TypeScript 語法,進而進一步掌握 ts的用法;

須要驗證函數參數類型,最基本的包括,stringnumberstring[],number[],還有元組( = > 進入元組的學習=>基本類型的學習) 和 JavaScript 同樣,TypeScript 函數能夠建立有名字的函數和匿名函數編程

function add(x:number,y:number):number {
  return x + y
}   //有名函數
let myAdd = function(x:number,y:number):number {
  return x + y
}   //匿名函數
複製代碼

咱們只對代碼右側的匿名函數進行了類型定義,而等號左邊的 myAdd 是經過賦值操做進行類型推斷出來的,書寫完整的函數類型。(類型推斷:若是沒有明確的指定類型,那麼 TypeScript 會依照類型推論(Type Inference)的規則推斷出一個類型。)json

let myAdd: (x:number,y:number) => number = 
function(x: number,y:number):number { return x + y}
複製代碼

函數類型包含兩部分: 參數類型和返回值類型;在 TypeScript 的類型定義中, => 用來表示函數的定義,左邊是輸入類型,須要用括號括起來,右邊是輸出類型,和 ES6 的箭頭函數不同後端

可選參數和默認參數

TypeScript 裏的每個函數參數都是必須的,傳遞給函數的參數個數必須與函數指望的參數個數一致,不然會報錯。數組

必填參數bash

function buildName(firstName:string,lastName:string) {
  return firstName + " " + lastName
}
let result0 = buildName(12, 12);    //提示 12 類型的參數不能賦值給 string
let result1 = buildName('Bob')    //提示應該有兩個參數,可是隻得到一個
let result2 = buildName('Bob','Adams','"sr')    //提示應該有兩個參數,可是隻得到三個
let result3 = buildName("Bob", "Adams");    //參數和傳入的參數同樣,不提示
複製代碼

可選參數數據結構

實現參數可選功能,咱們須要在參數名旁邊加 ?,可是可選參數必須跟在參數後面dom

function selectParam(firstName:string,lastName?:string) {
  return firstName + " " + lastName
}
let selectParam1 = selectParam('bob')
let selectParam2 = selectParam('bob','Adam')
let selectParam2 = selectParam('bob','Adam')    //兩個變量沒法從新聲明
複製代碼

默認參數

咱們能夠爲參數提供默認值,若是帶默認值的參數出如今必須參數前面,用戶必須明確的傳入 undefined 值來得到默認值

function param(firstName:string,lastName = 'Smith') {
  return firstName + ' ' + lastName
}
let param1 = param('bob')
let param2 = param('bob','Adam','kk')   //提示參數應該是1-2個
複製代碼

剩餘參數

必要參數,默認參數和可選參數都是表示某一個參數,有時候不知道要操做多少個參數,咱們能夠用 ... 來操做剩餘參數

function restParam (firstName:string,...restOfName:string[]) {
  return firstName + " " + restOfName.join(' ')
}
let employName = restParam ('Joseph',"Samuel","Bob")
//restParam 能夠換一種形式
let restParamFun:(fname:string,...rest:string[]) => string = restParam
複製代碼

this 和 箭頭函數(這裏好像有問題,待修改)

JavaScript 裏面 this 的值在函數被調用的時候指定。但 TypeScript 建立時候指定的

interface Card {
  suit: string;
  card: number;
}
interface Deck {
  suits: string[];
  cards: number[];
  createCardPicker(this:Deck):()=>Card;     //this 指向 Deck
}
let deck:Deck = {
  suits:['hearts','spades','clubs'],
  cards:Array(52),
  createCardPicker:function(this:Deck) {
    return () => {
      let pickedCard = Math.floor(Math.random()*52)
      let pickedSuit = Math.floor(pickedCard / 13);
      return { suit: this.suits[pickedSuit],card:pickedCard % 13}
    }
  }
} 
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
alert("card: " + pickedCard.card + " of " + pickedCard.suit);
複製代碼

若是是 JavaScript 會報錯,此時 this 指向了 window,可是TypeScript 不會報錯,他指定了 this 會在哪一個對象上面調用

基本類型

JavaScript 的類型分爲兩種:原始數據類型(BooleannumberstringnullundefinedSynmbol)和對象類型,在 TypeScript 中原始類型數據也是使用。爲了讓程序有價值,咱們須要可以處理最簡單的數據單元,數字,字符串

數字,字符串,數組

let decLiteral:number = 6   //數字類型
let name1:string = 'bob'    //字符串類型
let sentence:string = `Hello, my name is ${name1}`    //字符串模板
let list0:number[] = [1,2,3,4]    //[]形式定義數組
let list1:string[]=['12','12','90']
let list2:Array<number> = [1,23,4]    //Array<元素類型>
let list3:Array<string> = ['1','23','4']    //Array<元素類型>
複製代碼

TypeScript 中數組類型有多重定義方式,比較靈活

  • 類型 + 方括號 表示法
let fibonacci:number[] = [1,2,3,4]//只能傳number類型的,不然會提示錯誤
複製代碼
  • 2.數組泛型 (=> 跳到泛型去學習)
let fibinacci: Array<number> = [1,2,3,4]
複製代碼
  • 3.用接口表示數組 (=> 跳到接口去學習)
interface NumberArray {
 [index:number]: number
}
let fibonacci: NumberArray = [1,2,3,4]
複製代碼

NumberArray 表示:是一個數字數組,index 是數組的索引類型,: 後面表示是一個數字組成的數組(這樣表述好像還有點怪,歡迎指正)

元組 Tuple

元組類型容許表示一個已知元素數量和類型的數組,各元素的類型沒必要相同(數組合並了相同類型的對象,而元組合並了不一樣類型的對象)

let x:[string,number];
x = ['Hello',10]
複製代碼

枚舉:取值被限定在必定範圍內的場景

好比說一週只有七天

enum Color { Red,Green,Blue}
let c:Color = Color.Green
複製代碼

any

在編程階段還不清楚類型的變量指定一個類型,值多是動態輸入,可是 Object 類型的變量值容許你給她賦任意的值,不能在他的上面調用方法; 使用 any 類型會致使這個函數能夠接受任何類型的參數,這樣會丟失一些信息;若是咱們傳入一個數字,咱們只知道任何類型的值都有可能被返回

let list:any[] =  ['Xcat Liu', 25, { website: 'http://xcatliu.com' }];
let notSure:any = 4
notSure = "maybe a string instead"
notSure = false  
複製代碼

void 類型與 any 類型相反

他表示沒有任何類型,當有一個函數沒有返回

function warnUser():void {
  console.log("This is my waring message")
}
複製代碼

undefinednull ,它們的自己的類型用處不是很大: Never 類型表示的那些永遠不存在的值類型

斷言 as 相信我,我知道本身在幹什麼

let someValue:any = "this is a string"
let strLength:number = (<string>someValue).length//「尖括號」語法
let strLength1: number = (someValue as string).length;//一個爲as語法
複製代碼

聯合類型:表示取值能夠是多種類型中的一種

let myFavoriteNumber:string|number;// 鏈接符 |
myFavoriteNumber = 'seven'
myFavoriteNumber = 7
複製代碼

泛型

在軟件工程中,咱們不只要建立一致定義良好的 API,同時也要考慮可重用性,組件不只可以支持當前的數據類型,同時也能支持將來的數據類型,這在建立大型系統時爲你提供了十分靈活的功能 用泛型來建立可重用的組件;泛型是一種 特殊的變量,只用於表示類型而不是值

泛型函數

function identity<T>(arg:T):T {
  return arg;
}
let output = identity<string>("myString")
複製代碼

區別:泛型函數和非泛型函數沒有什麼不一樣,只是有一個類型參數在最前面,像函數聲明同樣

let myIdentity:<T>(arg:T) => T = identity
let myIdentity1:{ <T>(arg:T):T} = identity
複製代碼

可使用帶有調用簽名的對象字面量來定義泛型函數,咱們能夠將對象字面量拿出來做爲一個接口,將一個泛型參數當作整個接口的一個參數,這樣咱們就能清楚的知道使用的具體是哪一個泛型類型

泛型接口

interface GenericIdentityFn {
  <T>(arg:T):T
}
function identity<T>(arg:T):T {
  return arg
}
let myIdentity:GenericIdentityFn = identity
複製代碼

泛型類 (=>類的學習)

泛型類看上去和泛型接口差很少,泛型類使用(<>)括起泛型類型,跟在類名後面

class GenericNumber<T> {
  zeroValue:T,
  add:(x:T,y:T)=>T
}
let myGenericNumber = new GeneriNumber<number>()
複製代碼

類有兩個部分:靜態部分和實例部分,泛型類指的實例部分,因此靜態屬性不能使用這個泛型類型,定義接口來描述約束條件

泛型約束

interface Lengthwise {
  length:number
}
function loggingIdentity<T extends Lengthwise>(arg:T):T {
  console.log(arg.length)
  return arg
}
複製代碼

extends 繼承了一個接口進而對泛型的數據結構進行了限制

接口

TypeScript 核心原則之一是對值所具備的結構進行類型檢查,它是對行爲的抽象,具體行動須要有類去實現,通常接口首字母大寫。通常來說,一個類只能繼承來自另外一個類。有時候不一樣類之間能夠有一些共有的特性,這時候就能夠把特性提取成接口,用 inplements 關鍵字來實現,這個特性大大提升了面向對象的靈活性

可選屬性的好處:可能存在的屬性進行定義,捕獲引用了一個不存在的屬性時的錯誤

只讀屬性(readonly)

一些對象屬性只能在對象剛剛建立的時候修改它的值

interface Point {
  readonly x:number;
  readonly y:number;
}
複製代碼

TypeScript 具備 ReadonlyArray<T> 類型,它與 Array<T> 類似只是把全部的可變方法去掉了,確保數組建立後不再能被修改

readonly vs const

若是咱們要把他當作一個變量就使用 const ,若爲屬性則使用readonly

額外的屬性檢查

1.可使用類型斷言繞過這些檢查(斷言的兩種形式)

let strLength:number = (<string>someValue).length   //「尖括號」語法
let strLength1: number = (someValue as string).length    //一個爲 `as` 語法
複製代碼

2.使用索引籤,對象賦值給另外一個變量,對象字面量會被特殊對待並且會通過 額外屬性檢查,當將它們賦值給變量或做爲參數傳遞的時候

let squareOptions = { colour: "red", width: 100 }
let mySquare = createSquare(squareOptions)
複製代碼

3.添加字符串索引簽名

interface SquareConfig {
  color?:string;
  width?:number;
  [propName:string]:any
}
複製代碼

函數類型

接口可以描述 JavaScript 中對象擁有的各類各樣的外形,描述了帶有的普通對象以外,接口也能夠描述成函數類型;他有一個調用簽名,參數列表和返回值類型的函數定義,參數列表裏的每個參數都須要名字和類型,函數的參數名不須要與接口裏定義的名字相匹配,若是你沒有指定參數類型,TypeScript 的類型系統會推斷出參數類型

interface SearchFunc {
  (source:string,subString:string):boolean;
}
let MySearch:SearchFunc;
MySearch = function(source:string,subString:string) {
  let result = source.search(subString);
  return result > -1
}
複製代碼

可索引的類型

interface StringArray {
  [index:number]:string
}
//let myArray:StringArray = ["Bob",'Fred']
let myArray:StringArray;
myArray = ["Bob",'Fred']
複製代碼

類類型的實現接口,使用關鍵字 implements

interface ClockInterface {
  currentTime: Date;
}
class Clock implements ClockInterface {
  currentTime:Date;
  constructor(h:number,m:number){}
}
複製代碼

繼承接口 關鍵字extends

interface Shape {
  color:string;
}
interface PenStroke {
  penWidth:number;
}
interface Square extends Shape,PenStroke {
  sideLength:number
}
let square = <Square>{}
複製代碼

extends 是繼承某個類, 繼承以後可使用父類的方法, 也能夠重寫父類的方法;

implements 是實現多個接口, 接口的方法通常爲空的, 必須重寫才能使用

咱們引用的任何一個類成員的時候都用了 this,他表示咱們訪問的是類成員

類( Class ):定義一件事情的抽象特色,包括他的屬性和方法

對象( Object ):類的實例,經過 new 生成

面向對象( OOP )的三大特性:封裝,繼承,多態

封裝( Encapsulation ): 將對數據的操做細節隱藏起來,只暴露對外的接口,外界端不須要(也不可能)知道細節,就能經過對外提供的接口訪問該對象,同時也保證了外界沒法任意改變對象內部數據

繼承( Inheritance ):子類繼承父類,子類除了擁有父類的全部特性外,還有一些更具體的特性

多態( Polymorphism):由繼承而產生了相關的不一樣類,對同一個方法有不一樣的響應。好比 CatDog 都繼承自 Animal,可是分別實現了本身的 eat 方法。此時針對某一個實例,咱們無需瞭解它是 Cat 仍是 Dog,就能夠直接調用 eat 方法,程序會自動判斷出來應該如何執行 eat

存取器( getter & setter ):用以改變屬性的讀取和賦值行爲

修飾器( Modifiers ):修飾符是一些關鍵字,用於限定成員或類型的性質

抽象類(Abstract Class):抽象類是提供其餘類繼承的基類,抽象類不容許被實例化,抽象類的抽象方法必須在子類中被實現

接口(Interface):不一樣類之間公有的屬性和方法,能夠抽象成一個接口,接口能夠被類實現(implements),一個類只能繼承自另外一個類,可是能夠實現多個接口

class Greeter {
  greeting:string;
  constructor(message:string) {
    this.greeting = message
  }
  greet() {
    return "Hello" + this.greeting
  }
}
let greeter = new Greeter("World")
複製代碼

new 構造 Greeter 類的一個實例,調用以前定義的構造函數,建立一個Greeter 類型的新對象,執行構造函數初始化他

繼承

經過繼承來擴展示有的類,基類一般被稱做超類(Animal),派生類常被稱做子類(Dog

class Animal {
  name: string;
  constructor(theName: string) { this.name = theName; }
  move(distanceInMeters:number = 0) {
    console.log(`Animal moved ${distanceInMeters}`)
  }
}
class Dog extends Animal {
  constructor(name: string) { super(name); }
  bark() {
    console.log("woof woof")
  }
  move(distanceInMeters = 5) {
    super.move(distanceInMeters)
  }
}
const dog = new Dog()
dog.bark()
dog.move(10)
複製代碼

派生類包含一個構造函數,他必須調用 super() ,他會執行基類函數,在構造器函數裏訪問 this 的屬性前,必定要調用 super() 。這是 TypeScript 強制執行的一條重要規則

共有私有與受保護的修飾符

在全部 TypeScript 裏,成員都默認爲 public

當成員被標記成 private 時,他就不能在聲明他的外部訪問

protectedprivate 修飾符行爲很相似,可是有一點不一樣 protected 成員在派生類中仍然能夠訪問。

readonly 關鍵字將屬性設置爲只讀,只讀屬性必須在聲明或者構造函數裏被初始化

TypeScript 使用的是結構性類型系統,當咱們比較兩種不一樣的類型的時候,若是類型成員是兼容的,咱們就認爲他們類型是兼容的

存取器

TypeScript 支持經過 getters/setters 來截取對對象成員的訪問

let passcode = 'secret passcode'
class Employee {
  private _fullName:string;
  get fullName():string {
    return this._fullName
  }
  set fullName(newName:string){
    if (passcode && passcode == "secret passcode") {
      this._fullName = newName
    } else {
      console.log("Error: Unauthorized update of employee!");
    }
  }
}
let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
    alert(employee.fullName);
}
複製代碼

靜態屬性

當屬性只存在於類自己上面而不是類實例上,叫作靜態成員標識符 static

抽象類

做爲其餘派生類的基類使用,他們通常不會直接被實例化,抽象類中的抽象方法不包含具體實現而且必須在派生類中實現。抽象方法的語法和接口方法類似,都只是定義方法簽名,但不包括方法體。抽象類可包含成員的實現細節,必須包含 abstract 關鍵字標識和訪問修飾符

abstract class Animal {
  abstract makeSound():void
  move():void {
    console.log('roaming the earch...')
  }
}
複製代碼

把類當作接口使用

類定義會建立兩個東西:類的實例和一個構造函數,類能夠建立類型,因此你可以在容許使用接口的地方使用類

class Point {
  x:number;
  y:number;
}
interface Point3d extends Point {
  z:number
}
let point3d:Point3d = {x:1,y:2,z:3}
複製代碼

內置對象

JavaScript 中有不少內置對象,它們能夠直接在 TypeScript 中當作定義好了的類型

let b:Boolean = new Boolean(1)
let c:Error = new Error('Error occurred')
let d:Date = new Date()
let r:RegExp = /[a-z]/
複製代碼

DOMBOM 提供的內置對象,在 TypeScript 中會常常用到

暫時就先寫到這裏了,下次繼續,初學者不少不懂得地方,歡迎指正,學習交流

好像天天都在加班,回家老是很晚。促使我學 TypeScript 最主要的緣由是對代碼有着嚴格的要求,將某些未來可能會出現的 bug 扼殺在搖籃裏。在項目開發過程當中,我寫了一個公共的方法用來解析後端傳個人數據格式,突然有一天某個後端給個人數據結構從字符串變成了數組,就那麼一兩個接口的的數據結構變了,大部分的數據結構沒有變。致使頁面報錯,一行一行代碼排除,最後找到公共方法,花了我好長一段時間。那時候我就在想 java 多好呀,直接定義數據類型。避免了我這樣的狀況。後來我知道了 TypeScript 也能夠。慢慢的喜歡上他。對代碼有着嚴格的要求,提早 debug ,最近準備好好學,在忙都要學,可方便了。

在學習 TypeScript 官方文檔的時候,我類比 java 的語法學習,我本身感受語法挺像的。我還總是問個人同事,大家 java 裏面是否是有那個語法 implementsextends , 還請教了她們在 java 它們的區別。個人同事覺得我在學 java ,我回她們說類比學前端

TypeScript 資料求推薦,資源共享,看了兩遍官方文檔,之後準備結合項目進行實戰。若是你有相關的開發經驗,想像你學習,交流哈哈,須要一個老司機帶我哈哈

相關文章
相關標籤/搜索