bunny筆記|TS基礎(chǔ)(2):泛型函數(shù)、泛型約束、在泛型約束中使用參數(shù)/使用類型、keyof、typeof等

01

typescript的操縱類型

//從類型中創(chuàng)建類型:泛型類型、keyof類型操作符、typeof操作類型符、索引訪問類型、條件類型、映射類型、模板字面量類型
//我們通過模板字面量的字符串來改變屬性的映射類型


//軟件工程的一個主要部分是建立組件,這些組件不僅有定義明確和一致的api,而且還可以重復(fù)使用,能夠處理今天的數(shù)據(jù)和明天的數(shù)據(jù),這些組件將為我們建立大型軟件系統(tǒng)提供最靈活的能力,比如像c#和java這樣的語言中,創(chuàng)建可重用組件的工具箱中有一個非常重要的工具,就是泛型。也就是說能夠創(chuàng)建一個在各種類型上工作的組件,而不是單一的類型,這就使得用戶可以消費(fèi)這些組件,并且使用自己的類型。
/**例如:
function identity<type>(arg:Type):Type{
    return arg;
}
*/
//在函數(shù)名后添加一個Type的類型,這個Type允許我們捕獲用戶提供的類型,比如number,string,這樣我們就可以在以后使用這些信息了,這里我們再次使用Type作為返回值,經(jīng)過檢查,我們看到參數(shù)的類型和返回的類型使用的是相同的type這個類型,這就使得我們可以講類型信息從函數(shù)的一側(cè)輸入,然后從另一側(cè)輸出。我們說這個版本的函數(shù)是通用的,因?yàn)樗谝幌盗械念愋蜕厦婀ぷ骱臀覀兪褂胊ny類型不同的是,它和我們第一次使用的number這個參數(shù)和返回number類型的identity,它的作用是一樣的精確,也就是說它不會丟失任何的類型信息。寫好通用函數(shù),我們就可以使用兩種方式來調(diào)用它了.
//第1種方式:我們將所有的參數(shù)包括類型參數(shù)都傳遞給函數(shù),如
/**
  function identity<Type>(arg:Type):Type{
    return arg
}
let output=identity<string>("myString")//方式1
let output=identity("myString")//方式2
 */

//我們看在identify調(diào)動的時候加上一個<>定義泛型類型,這里寫了<string>然后在()里傳入實(shí)參

//第2種方式:使用類型參數(shù)推斷,也就是說我們希望編譯器根據(jù)我們傳入的參數(shù)的類型,自動為我們設(shè)置這開發(fā)的值的類型,我們把string去掉

//使用通用類型
function loggingIdentify<Type>(arg: Type): Type {
    //console.log(arg.length);//報錯,泛型類型Type上不存在length。但實(shí)際上是可以獲取length的,但ts語法上不允許這樣表達(dá)
    return arg;
}

//如果用戶傳入數(shù)組可以求值length,但是如果用戶傳入number那就獲取不到length了,那就會產(chǎn)生問題,要傳入數(shù)組,應(yīng)如下改造:

function loggingIdentify2<Type>(arg: Array<Type>): Type[] {
    console.log(arg.length);
    return arg;
}
loggingIdentify2([1,2,3,4])

02

function identify<Type>(arg:Type):Type{
    return arg
}

//定義泛型函數(shù)的形式:()=>
//let myIdentity:()=>identify
//let myIdentity:(arg:Type)=>identify
let myIdentity:<Type>(arg:Type)=>Type=identify

//寫成對象字面量的形式:{}=
//let myIdentity2:{key值}=identify
let myIdentity2:{<Type>(arg:Type):Type}=identify

//將泛型字面量的形式寫成泛型接口
interface GenericIdFn{
    // ():Type
    <Type>(arg:Type):Type
}

let myIdentity3:GenericIdFn=identify

//接口方式的另一個寫法,更靈活一些
interface GenericIdFn2<Type>{
    (arg:Type):Type
}

let myIdentity4:GenericIdFn2<string>=identify

03

class GenericNumber<NumType>{
    zeroValue: NumType
    add: (a: NumType, y: NumType) => NumType
}

let myGeneric = new GenericNumber<number>()
myGeneric.zeroValue = 0
myGeneric.add = function (x, y) {
    return x + y
}

let myGeneric2 = new GenericNumber<string>()
myGeneric2.zeroValue = ''
myGeneric2.add = function (x, y) {
    return x + y
}

04

泛型約束

interface Lengthwise{
    length:number
}

function loggingId<Type extends Lengthwise>(arg: Type): Type{
    console.log(arg.length);
    return arg;
}

loggingId(['hello',2,22])

05

在泛型約束中使用類型參數(shù)

function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) {
    return obj[key]
}

let x = {
    a: 1,
    b: 2,
    c: 3
}

getProperty(x, 'a')


06

在泛型中使用類類型


// function creat<Type>(c: { new(): Type }): Type {
//     return new c();
// }

class Beekeeper {
    hasMask: boolean = true
}

class Zoo {
    nametag: string = 'Miki'
}

class Animal {
    numLegs: number
}

class Bee extends Animal {
    //定義一個屬性,實(shí)例類
    keeper: Beekeeper = new Beekeeper()
}

class Lion extends Animal {
    keeper: Zoo = new Zoo()
}

//形參傳入一個類,并返回類
function creatInstance<A extends Animal>(c:new()=>A): A {
    return new c();
}

creatInstance(Lion).keeper.nametag
creatInstance(Bee).keeper.hasMask
//creatInstance(Beekeeper)//報錯,Beekeeper沒有繼承Animal

07

keyof類型操作符

type Point = { x: number; y: number };
type P = keyof Point;

const p1: P = 'x'
const p2: P = 'y'

//所以type P = keyof Point;的P的類型是 "x"|"y"聯(lián)合類型

//索引簽名

type Arrayish={
    [n:number]:unknown
}

type Mapish={
    [k:string]:boolean
}

type M=keyof Mapish

const m1:M='sss'
const m2:M=177
// const m3:M=false//報錯,不能將類型boolean分配給類型"string|number"

08

typeof

//了解原生的type,console.log(typeof 'helloWorld');

// let s = "hello"
// let n: typeof s
// n = 'helll'
// n = 777 //報錯,number不能分派給string類型

//預(yù)定義類型
//ReturnType<T>

// type Predicate = (x: unknown) => boolean
// type K = ReturnType<Predicate>

// function f() {
//     return {
//         x: 99,
//         y: 22
//     }
// }

// type PP=ReturnType<typeof f>
// const hh:PP={x:3,y:99}
// const p:PP=100//報錯:不能將number分配給類型“{x:number,y:number}”

function msg() { }
let shouldC: typeof msg
//shouldC = 100 //報錯:不能將類型number分配給類型()=>void

//注意:我們絕對不能在typeof的后邊去調(diào)用這個函數(shù)試圖去返回函數(shù)的返回結(jié)果的類型,這是由問題的。也就是說tyof只能是去修飾一個變量或者某個對象里的屬性,不能修飾函數(shù)體類型

09

type Person = {
    age: number,
    name: string,
    alive: boolean
}

//通過索引的方式訪問Person里面age屬性
type Age = Person['age']
let age: Age = 99
//只能訪問number類型

type Person2 = {
    age: number,
    name: string,
    alive: boolean
}

type L1 = Person2['age' | 'name']
let L11: L1 = 88
//可以訪問number和string兩種類型

type L2 = Person2[keyof Person2]
//age、name、alive三種類型都可訪問


const MyArray = [
    { name: 'bunny', age: 18 },
    { name: 'heihei', age: 99 }
]

type Person3 = typeof MyArray[number]
let aaa:Person3={
    name:'bbb',
    age:44
}


//只能在類型使用索引

10

interface Animal {
    live(): void
}

interface Dog extends Animal {
    woof(): void
}

//type Example1=number
type Example1 = Dog extends Animal ? number : string

//type Example2=string
type Example2 = Dog extends Animal ? number : string

//泛型條件類型

interface IdLabel {
    id: number
}

interface NameLabel {
    name: string
}

type NameOrId<T extends number | string> = T extends number ? IdLabel : NameLabel

function createLable<T extends number | string>(idOrName: T): NameOrId<T> {
    throw ''
}

//type a=NameLable
let a = createLable('typescript')
//tyoe b=IdLabel
let b = createLable(25)

//type c =NameLable | IdLabel
let c = createLable(Math.random() > 0.9 ? 'hello bunny' : 250)

11

//條件類型約束

//type MessageOf<T>=T['message']
 type MessageOf<T extends {message:unknown}>=T['message']
 
 interface Email{
     message:string
 }

 type EmailMessageContents =MessageOf<Email>

 //如果我們想要MessageOf來接收任何類型,并且在Message這個屬性不可用的情況下,把它默認(rèn)為返回一個never類型,這時候就需要一個約束條件移出再引入另外一個條件類型了

 type MessageOf2<T>=T extends {message:unknown}?T['message']:never
 
 interface Email2{
    message:string
}

interface Dog{
    bark():void
}
type EmailMessageContents2 =MessageOf2<Email2>
const emc:EmailMessageContents2='hvdsjcvcf'

type DogMessageContents = MessageOf2<Dog>
// const dmc:DogMessageContents ='erro' //報錯,不能將string分配給類型never ,可以加斷言

const dmc:DogMessageContents ='erro' as never

12

在條件類型內(nèi)進(jìn)行推斷

 type GetReturnType<Type>=Type extends (...arg:never[])=>infer Return?Return:number

 //type Nmu =number
 type Num =GetReturnType<()=>number>
 let num:Num =100

//type Nmu =string
 type Str = GetReturnType<(x:string)=>string>
 let str:Str=''

 //type Bools=boolean[]
 type Bools=GetReturnType<(a:boolean,b:boolean)=>boolean[]>
 let bools:Bools=[false,true]

 //type Never=never
 type Never = GetReturnType<string>
 let never:Never ='error' as never

function sn(x:string):number
function sn(x:number):string
function sn(s:string|number):string|number
function sn(s:string|number):string|number{
    return Math.random()>0.5 ? 'hello':23
}

//type T1=Return
type T1=ReturnType<typeof sn>
//const t1:T1=true//報錯,不能將boolean分配給string|number

13

分布式條件類型


type ToArray<T> = T extends any ? T[] : never

type SN = ToArray<string | number>

//type SN = string[]|number[]
//type SN = (string|number)[]
//type SN =ToArray<string|number>
let saon: SN = []

//非分發(fā)式的ToArray

type TADist<T> = [T] extends [any] ? T[] : never
type SAON = TADist<string|number>

let san1:SAON =['1',22]
//let san:SAON =[true]//報錯,不能將類型boolean分配給string|number

tsconfig.json

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "rootDir": "./src",
    "outDir": "./dist",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "strictPropertyInitialization": false,
    "skipLibCheck": true
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容