1. 程式人生 > 實用技巧 >第二章-TypeScript 基礎語法入門

第二章-TypeScript 基礎語法入門

Typescript 的定義

官網對 typescript 的定義:Typescript 是 JavaScript 的超集,它可以編譯成純 Javascript 。就是說,Typescript 可以使用 JavaScript 的語法,並且對 JS 語法進行了擴充套件,而且瀏覽器並不能直接解析出 TS 程式碼,必須編譯成 JS 才可以執行。中文文件

ts 程式碼

// ts 語法,ts 是靜態型別的
let a = 123; // 定義 a 為數字型別
// 標準寫法為 let a: number = 123;
a = '123'; // 此時 a 為字串型別,所以會報錯
a = 456; // 如果設定 a 為字串型別則不會報錯

js 程式碼

// js 語法,js是動態型別的語言
let a = 123;
a = '123'; // 預設轉換為字串型別,不會報錯

Typescript 的優勢

  • 編寫程式碼過程中,更好的錯誤提示
  • 更加友好的編輯器提示
  • 程式碼語義更加清晰

js程式碼

function demo(data) {
  return Math.sqrt(data.x ** 2 + data.y ** 2)
}

demo(); // 不傳引數會報錯,但編輯器沒有相應的錯誤提示

ts程式碼

// 可以將引數的格式定義在外面
interface Point {x: number, y: number}

// 為引數定義型別,傳參的時候,必須以這種格式傳參,否則編輯器提示錯誤資訊
// 為引數明確型別,編輯器的提示會更加友好,同時也使程式碼的語義化更加清晰
function tsDemo (data: Point) {
  return Math.sqrt(data.x ** 2 + data.y ** 2)
}

tsDemo({x: 1, y:2}); // 在ts中如果不傳引數,編輯器會有報錯提示

Typescipt 基礎環境搭建

  • 首先需要下載安裝 nodejs
  • 在 vsCode 中找到設定,搜尋 quote 和 tab 設定編輯 typescript 程式碼為單引號,tab縮排為 2 空格。下載prettier外掛,搜尋設定 preitter->single quote 勾選,然後搜尋 formateOnSave 設定並勾選,然後每次儲存就會格式化程式碼。
  • 然後執行 npm install [email protected] -g 安裝 typescript,然後使用tsc demo.ts 可以將 demo.ts 編譯為 demo.js,然後使用 node demo.js 可以打印出demo.js 中的內容
  • 上述編譯執行ts程式碼過於複雜,安裝 npm install -g ts-node 工具,然後直接執行 ts-node demo.ts 即可直接列印ts程式碼

靜態型別的深度理解

我們定義了一個常量 count ,它的型別為 數字型別,那麼它就 number 型別所有的屬性和方法。

const count: number = 2019
console.log(typeof count.toString()) // 結果為 string

我們使用 interface 可以自定義一個型別,那麼這個型別的例項必須是這種型別的資料,並且其例項可以使用該型別所有的屬性和方法。

interface Point {
  x: number
  y: number
}
const point: Point = {
  x: 3,
  y: 4,
}
console.log(point.x)

基礎型別和物件型別

  • 基礎型別 null, undefined, symbol, boolean, viod
  • 物件型別 {}, Class, function, []
// 基礎型別 null, undefined, symbol, boolean, viod
const count: number = 123
const myName: string = 'aurora'

// 物件型別
const teachr: {
  name: string
  age: number
} = {
  name: 'aurora',
  age: 18,
}

// numbers 是一個數組,陣列中的每一項都是數字型別
const numbers: number[] = [1, 2, 3, 4, 5]

class Person {}
const aurora: Person = new Person()

// getTotal 是一個函式,返回值是數字型別
const getTotal: () => number = () => {
  return 123
}

型別註解和型別推斷

在使用 TS 的時候我們就是希望變數以及屬性的型別固定,因此就有了型別註解和型別推斷。

  • 型別註解 type annotation: 我們告訴TS變數是什麼型別的
  • 型別推斷 type inference: TS會自動地去嘗試分析變數的型別
  • 如果 TS 能夠自動分析變數型別,我們就不需要新增型別註解了
  • 如果 TS 無法分析變數型別的話,我們需要為定義的變數新增型別註解
// 型別推斷:TS 可以推斷出這三個變數的型別為 number
const firstNumber = 1
const secondNumber = 2
const total = firstNumber + secondNumber

// 型別註解:TS 無法判斷引數的型別,因此需要新增型別註解,明確引數的型別
function getTotal(firstNumber: number, secondNumber: number) {
  return firstNumber + secondNumber
}
const total = getTotal(1, 2)

非必要不使用型別註解,儘量讓TS自己判斷型別.

函式相關型別

TS 的函式定義與 JS 定義函式的方式基本一致。

function hello() {}
const hello1 = function () {}
const hello2 = () => {}

函式返回值的型別與引數並不完全相關,下面介紹幾種返回值的型別。

// 指定函式的返回值為 number 型別,如果不指定當返回值為其他型別的時候,就不會提示錯誤資訊
function add(first: number, second: number): number {
   return first + second
}
const total = add(1, 2)
// void 表示這個函式不應該有返回值,如果寫了 return 則提示錯誤資訊
function sayHello(): void {
  console.log('hello')
}
// never 永遠也執行不到最後一行程式碼
function errorEmitter(): never {
  // throw new Error()
  // console.log(123)
  while (true) {}
  console.log('123')
}
// 解構賦值的型別註解
function add({ first, second }: { first: number; second: number }) {
  return first + second
}

function getNumber({ first }: { first: number }) {
  return first
}

const total = add({ first: 1, second: 2 })

基礎語法複習

函式返回值的寫法

// 這種寫法 TS 能夠型別推斷出返回的結果為 number 型別,因此函式的型別註解可以省略
const func = (str: string): number => {
  return parseInt(str, 10)
}
// 冒號後面是型別註解, 等號後面是按照型別註解格式編寫的程式碼
const func1: (str: string) => number = (str) => {
  return parseInt(str, 10)
}

其他型別補充:

// data 型別
const date = new Date()

interface Person {
  name: 'string'
}
// TS 無法識別出 JSON 格式的型別,我們可以自定義一個型別
const rawData = '{"name": "aurora"}'
const newData: Person = JSON.parse(rawData)

// 設定 temp 的型別可以是 number 或者 string
let temp: number | string = 123;
temp = 'str'

陣列和元組

  • ts 中的陣列和 js 陣列的定義方式完全一致
  • ts 中引入了元組(tuple)的概念, 使用元組可以更好的對陣列的內容進行約束
// 陣列
const arr: (number | string)[] = [1, '2', 3]
const str = ['a', 'b', 'c']
// 類型別名: type alias
type User = { name: string; age: number }
const objArr: User[] = [
  {
    name: 'aurora',
    age: 18,
  },
]
// 藉助class進行型別註解
class Teacher {
  name: string
  age: number
}
const teacherArr: Teacher[] = [
  new Teacher(),
  {
    name: 'aurora',
    age: 20,
  },
]
--------------------------------------------------
// 元組 tuple 使用元組可以更準確地約束陣列中的每一項內容
const personInfo: [string, string, number] = ['aurora', 'male', 20]
// csv 格式
const personList: [string, string, number][] = [
  ['aurora', 'male', 21],
  ['tony', 'male', 30],
  ['amy', 'female', 25],
]

interface 介面

TypeScript的核心之一就是型別檢查。interfacetype 都可以定義型別註解。在TypeScript裡,介面的作用就是為這些型別命名和為你的程式碼或第三方程式碼定義契約並讓程式碼看起來更好理解。

介面(interface)和類型別名(type alias)

  • interface 可以定義一個函式或者物件,無法直接定義基礎型別
  • type 類型別名可以定義基礎型別和物件型別
  • 在 TS 中如果能用 介面(interface)表示型別就用介面表示型別,否則使用type
// interface 可以定義一個函式或者物件,無法直接定義基礎型別
interface Person {
  // readonly 表示這個屬性只能讀不能寫
  // readonly name: string
  name: string
  // 屬性後面新增 ? 表示這個屬性是可選的
  age?: number
  // 定義任何額外的屬性
  [propName: string]: any
  // 定義一個方法,返回值是string型別
  say(): string
}

// type 類型別名可以定義基礎型別和物件型別
type Student = {
  name: string
}
type num = number

介面的使用:

  • 介面可以繼承

    interface Teacher extends Person {
      teach(): string
    }
    
  • 介面可以直接定義一個方法

    interface SayHi {
      // 引數是一個 string 型別,返回值是一個 string 型別
      (word: string): string
    }
    
  • 一個class想要使用 interface,可以使用 implements 屬性

    class User implements Person {
      name = 'zhangsan'
      say() {
        return 'hello'
      }
    }
    

類的定義與繼承

  • TS 中類的定義和繼承與 ES6 中類基本一致。
  • 子類使用 extends 關鍵字繼承父類的屬性和方法
  • 子類可以重寫父類的方法
  • 當我們重寫了父類的方法然後還想要使用父類的方法的時候,可以通過super關鍵字呼叫父類中的方法
class Person1 {
  name = 'aurora'
  getName() {
    return this.name
  }
}
class Teacher1 extends Person1 {
  getTeacherName() {
    return 'teacher'
  }
  // 子類把父類的getName覆蓋了(重寫了)
  getName() {
    // super 關鍵字相當於父類的例項
    // super 的作用:當重寫了父類的方法,但是還想要使用父類的方法時,使用super呼叫
    return super.getName() + ' abc'
  }
}
const teacher = new Teacher1()
console.log(teacher.getName()) // aurora abc
console.log(teacher.getTeacherName()) // teacher

類中的訪問型別和構造器

  • TS 中的訪問型別有: public protected private
  • public 允許在類的內外被呼叫
  • private 允許在類內被使用
  • protected 允許在類內及繼承的子類中使用
  • 如果在屬性或者方法之前沒有新增訪問型別, 那麼預設為 public
class Person2 {
  protected name: string | undefined
  // 如果在屬性或者方法之前沒有新增訪問型別,預設是 public
  public say() {
    this.name
    console.log('hey')
  }
}
class Teacher2 extends Person2 {
  public sayBye() {
 	// Teacher2 類繼承成了 Person2 ,因此這裡不報錯    
    this.name
  }
}
const per = new Person2()
// 由於在Person2類中定義了name的訪問屬性為 protect, 因此這裡報錯
per.name = 'aurora'
console.log(per.name)
// 可以使用say() 方法
per.say()

constructor 構造器的使用

class Person2 {
  // 傳統寫法
  public name: string | undefined
  constructor(name: string) {
  	this.name = name
  }

  // 簡化寫法
  constructor(public name: string) {}
}

const per = new Person2('aurora')
console.log(per.name)

super 用法

  • super(引數) 表示呼叫父類的 constructor
  • 如果父類的 constructor 不存在也需要呼叫 super() 否則報錯
class Person2 {
  constructor(public name: string) {}
}

class Teacher2 extends Person2 {
  constructor(public age: number) {
    // super(引數) 表示呼叫父類的 constructor
    // 如果父類的 constructor 不存在也需要呼叫 super() 否則報錯
    super('aurora')
  }
}

const teacher2 = new Teacher2(20)
console.log(teacher2.name)
console.log(teacher2.age)

靜態屬性: Setter 和 Getter

使用 getter 和 setter 可以獲取和設定類中的私有屬性.

class Person3 {
  constructor(private _name: string) {}
  get name() {
    return this._name
  }
  set name(name: string) {
    const realName = name.split('_')[0]
    this._name = realName
  }
}

const per = new Person3('aurora')
console.log(per.name) // aurora
per.name = 'aurora132_ql'
console.log(per.name) // aurora132

單例模式

單例模式: 只生成一個例項

// 單例模式 只能生成一個例項
// static 將屬性/方法直接掛載到 Demo 類上
class Demo {
  // 建立一個屬性,它的型別為 Demo 並把它掛載到Demo類上
  private static instance: Demo
  private constructor(public name: string) {}

  static getInstance(name: string) {
    if (!Demo.instance) {
      Demo.instance = new Demo(name)
    }
    return Demo.instance
  }
}

const demo = Demo.getInstance('aurora')
const demo1 = Demo.getInstance('ql')
console.log(demo) // Demo { name: 'aurora' }
console.log(demo1) // Demo { name: 'aurora' }

readonly 和 抽象類

在類中的某一屬性之前新增一個 readonly 屬性, 可以限制該屬性為只讀屬性, 不能被修改.

class Person4 {
  // readonly 限制屬性只讀
  public readonly name: string
  constructor(name: string) {
    this.name = name
  }
}

const person4 = new Person4('aurora')
// person4.name = 'hello' // 此處報錯,name 屬性只讀
console.log(person4.name)

抽象類

抽象類實際上是一些公用方法的封裝, 可以被繼承

abstract class Geom {
  getType() {
    return 'Geom'
  }
  // 抽象方法
  abstract getArea(): number
}

class Circle extends Geom {
  getArea() {
    return 123
  }
}

class Square extends Geom {
  getArea() {
    return 456
  }
}