typescript修煉指南(二)

Alleyine發表於2019-12-05

typescript修煉指南(二)

大綱

本章主要講解一些ts的常規用法,涉及以下內容:

  • interface: 介面
  • class : 類
  • function: 函式
  • T : 泛型

之前因本人業務繁忙(太菜了 被拉去改bug了┭┮﹏┭┮), 所以拖更了,後邊會陸續補上, 還在踩坑的小夥伴,希望本文能給你們一些幫助 QAQ


介面

基本使用

定義: 用來集中約束資料型別,用關鍵詞interface來定義一個介面

  interface People {
        num:number,
        country: string,
    }
複製程式碼

使用介面:

 // 這樣就會報錯: 因為約定的時候沒有name屬性
 const getPeople = (people: People) => people.name 
複製程式碼
  • 定義可選屬性
interface User {
        name: string,
        age: number,
        sex?: string  // ? 表示可選的
    }
複製程式碼
  • 定義只讀屬性
  interface User {
        name: string,
        age: number,
        readonly weight: string, // readonly表示只讀
  }
複製程式碼
  • 定義函式屬性
interface User {
        name: string,
        age: number,
        get: (sex: string)  => string  // 定義引數型別和返回值型別
 }
 
 // 或者直接使用函式介面
interface Func {
    (sex: string) : string  // 注意這裡的符號
}

interface User {
    name: string,
    age: number,
    get: Func,  // 使用介面
}
複製程式碼
注意點

有時候我們會遇到這樣的情況,當呼叫函式的時候,傳入的引數型別有可能是可選型別,舉個例子

  • 定義介面
interface User1 {
        name?: string, 
}
複製程式碼
  • 定義函式
 const getUser = (user: User1): { result: string} => {
        return {result: user.name }
 }
複製程式碼

這時候會報型別錯誤, 因為返回值也可能是個void型別, 那如何避免?返回的時候也是可選即可

 const getUser = (user: User1): { result?: string} => {
        return {result: user.name }
 }
複製程式碼

另外, 當我們呼叫的時候傳入的是額外的屬性比如傳入age屬性, 則需要as斷言

  const username = getUser({age: 20} as User1)
複製程式碼

或者,我們可以在介面定義的時候傳入一個自定義屬性約束

 interface User1 {
        name?: string, 
        [propName: string]: any,// 屬性名字串型別, 這種條件下值可以是任意型別了
  }
複製程式碼

通過上邊約束, 我們就可以實現這樣的一個資料型別約束,鍵key是變化的, key自增變化

const UserP = {
    name: 'lili',
    age: 20,
    userClass: {
        1: 'class1',
        2: 'class2',
       // .....
    }
}
複製程式碼
  • 定義約束
 interface mark {
        [name: number]: string 
}
    
interface User2 {
    name: string, 
    age: number,
    userClass: mark
}
複製程式碼
介面繼承

直接貼程式碼,比較簡單, 也可以多繼承的

interface inter1 {
    name: string
}

interface inter2 {
    (age: number) : number
}

interface inter3 {
    get: (name: string) => string
}

interface interAll extends inter1 {
    add: (age: number) => number
}
// 多繼承
interface interAll extends inter1, inter2, inter3 {
    add: (age: number) => number
}
複製程式碼

類class

抽象類

抽象類中只用於定義,定義的抽象方法,不會實現具體的方法,且不能例項化,只能作為基類,關鍵詞:abstract

abstract class Test {
    add(): void { console.log('add') }
    // 抽象方法不需要實現具體的方法
    abstract remove(): string 
}

// 直接例項化會導致錯誤
// const test = new Test()
複製程式碼

繼承抽象類

class Test1 extends Test {
    // 這裡如果不實現remove方法就會報錯, 因為是抽象方法
    remove() {
        return 'remove'
    }
}
複製程式碼
ts中的類關鍵詞
  • public: 公共修飾詞 預設都是public 外部可以訪問到的
  • protected: 外部無法訪問 內部和子類都可以訪問
  • private: 私有修飾符 只有內部可以訪問
// class中的 關鍵詞
class Test2 {
    // 公共修飾詞 預設都是public 外部可以訪問到的
    public add() {
        console.log('add')
    }

    //  外部無法訪問 內部和子類都可以訪問
    protected remove() {
        console.log('remove')
    }

    // 私有修飾符 只有內部可以訪問
    private result() {
        console.log('result')
        this.remove() // 可以訪問
    } 
}

class Test3 extends Test2 {
    change() {
        this.add() 
        this.remove()
        // 報錯  子類無法訪問
        //this.result()
    }
}

const test2 = new Test2()
test2.add()
//test2.remove() // 報錯
//test2.result() // 報錯
複製程式碼
作為初始化介面
class Test4 {
    public name: string = 'lili'
    public age: number = 20
    public get(): string { return name }
}

const test4 = new Test4()
console.log(test4.get())
// test.name = 1 // 報型別錯誤  因為name是string型別
複製程式碼

函式

  • 可選引數
const f1 = (a: string = 'f1', b? : number): void => console.log(a,b?b:'')
複製程式碼
  • 剩餘引數 ...rest
 const f2 = (a: string, ...rest: string[]): void => { }
複製程式碼
  • 函式的過載
// 呼叫批次
function reloadFunc (name: string): { code: number }
function reloadFunc (name: string, age: number): { code: number } 
// 函式實現
function reloadFunc (name: string, age?: number, sex?: string): { code: number } {
    if(name) {
        return { code: 0 }
    }else if(age) {
        return { code: 1 }
    }else {
        return { code: 2 }
    }
}

reloadFunc('lili')
reloadFunc('lili', 20)
//  reloadFunc('lili', 30, '男')  這一行會報一個錯誤: 期望是2個引數
複製程式碼

泛型

基本使用

泛型最大的作用在於靈活,對於程式碼複用有很大的用處,說白了,型別由呼叫者決定,從而實現的一種約束 定義: (T代表一種資料型別)

function testT<T>(name: T): T {
    return name
}

// 呼叫, 傳入的是string型別 返回的約束也是該型別
testT('lili') 
複製程式碼

同時支援多個型別的傳入:

function testT1<T, F>(name: T, age: F): [T, F] {
    return [name, age]
}
複製程式碼

如果傳入的是陣列型別,需要返回陣列的某個屬性,比如length屬性

 function testT2<T, F>(arr: Array<T>): number {
    return arr.length  // 如果沒指明陣列會報不存在length屬性錯誤 加上Array即可
}
複製程式碼
在介面中的應用
interface testT3<T> {
    name: T
}

// 使用它
const test_t: testT3<number> = { name: 0 }
複製程式碼
泛型類
class TestT4<T> {
    private name: T[] = [] 

    public test(age: T): T {
        return age
    }
}
複製程式碼

呼叫就可以這樣

const test_t1 = new TestT4<number>()
test_t1.test(10)
複製程式碼
泛型繼承

也叫做泛型的約束,約束泛型只能在某個型別範圍內,使用關鍵詞extends

// extends 繼承某個型別
class TestT5<T extends Args> {
    private name: T[] = [] 

    public test(age: T): T {
        return age
    }
}
const test_t2 = new TestT5<number>() // 這裡就必須是這兩種型別了
複製程式碼
泛型物件索引

比如我們傳入一個物件型別,這種情況下編譯器是懵逼的 不知道obj有沒有key(預設是{})屬性且還不知道key的型別

function testT6<T>(obj: T, key: number) {
     return obj[key]       
}
複製程式碼

改造一下,讓它繼承物件的型別,在用屬性繼承(keyof)這個型別,可能難理解, 直接看程式碼

 function testT6<T extends object, U extends keyof T>(obj: T, key: U) {
    return obj[key]
 }
複製程式碼
泛型繼承介面

泛型並不支援多繼承,也就是說T extends A,B,C 這樣是行不通的, 但是我們可以先用介面實現多繼承,然後在繼承這個介面, 也就是說 interface A extends B,C,D 然後 T extends A

interface testFunc extends A,B,C{
    name: string,
    age: number,
}

function testT7<T extends testFunc>(obj: T) {
    return obj
}
複製程式碼
泛型建構函式約束

如果傳入的引數是一個建構函式比如:

 function testT8<T>(func: T) {
     return new func() // 這樣會報錯, 因為不知道是構造型別
}
複製程式碼

改造一下: ( new( ) )

function testT8<T>(func: {new(): T}): T {
    return new func() 
}
複製程式碼

如果對大家有幫助記得點贊個~ , 如有錯誤請指正, 我們一起解決,一起進步 ~

相關文章