前言
之前一直使用的是 js 現(xiàn)在轉(zhuǎn)過(guò)來(lái)學(xué)習(xí) ts 的時(shí)候嘗到了 ts 對(duì)類型規(guī)范的很多好處孽查,相應(yīng)的 ts 的類型有時(shí)候也讓人頭大,下面簡(jiǎn)單總結(jié)一下自己對(duì) ts 高級(jí)類型的學(xué)習(xí)成果茸苇。
一般類型
//給變量指定類型
const value: string = "value"
//給方法的形參和返回值指定類型
const fun0 = (str: string): string => {
return ""
}
//甚至可以直接給方法指定類型
type funType = (str: string) => string
const fun1: funType = (str) => {
return ""
}
這個(gè)時(shí)候我們可以看的 fun1
這時(shí)候是不用指定 str
的類型的置济,IDE會(huì)自動(dòng)提示迂求,如下:
但還有個(gè)特殊的情況 fun1
的形參是可以不寫(xiě)的,比如:
type funType = (str: string) => string
const fun2: funType = () => {
return "fun2 return value"
}
console.log(fun2("value"));
這樣是不會(huì)報(bào)錯(cuò)的虱肄,可是為什么呢距帅?我們明明指定了funType
的形參有且是string
,那大家肯定想問(wèn)真是為什么匾寝,抱歉我也不知道搬葬,哈哈哈!如果有知道的朋友希望能留言告知艳悔。這樣做的問(wèn)題是在fun2
里面是拿不到傳入的形參的急凰,不知道是不是bug。
泛型
對(duì)就是它猜年,我個(gè)人來(lái)說(shuō)在學(xué)習(xí)和使用ts
的途中最頭疼的就是泛型抡锈,ts
中泛型的使用真的讓人頭暈眼花,但當(dāng)你真的看明白ts
中泛型的使用是又不得不說(shuō)這簡(jiǎn)直就像是在變魔術(shù)乔外。下面記錄一下泛型的一般用法:
//將對(duì)象中值為number類型的數(shù)據(jù)組成一個(gè)新對(duì)象
function getObjAllNum<T>(t: T): any {
let numObj = {} as any;
for (const tKey in t) {
if (typeof t[tKey] == "number") {
numObj[tKey] = t[tKey]
}
}
return numObj
}
const value = {
name: 'zl',
age: 27,
sex: 'm'
}
//輸出 {age: 27}
console.log(getObjAllNum(value));
可以看到床三,當(dāng)我們調(diào)用getObjAllNum
的時(shí)候T
類型已經(jīng)轉(zhuǎn)變成對(duì)象value
的類型了。
高級(jí)類型
接下來(lái)要說(shuō)的就是ts
為我們提供的一些類型的高級(jí)操作方式
類型別名(type)
可以理解成可以給一個(gè)類型再取另一個(gè)名字杨幼,比如:
type myString = string
const str: myString = "str"
聯(lián)合類型(|)
A|B
表示A
或者B
中的任意一個(gè)類型 撇簿,如下代碼,zhangSan
就是屬于Man
類型差购,xiaoLi
屬于Woman
類型四瘫。
interface Man {
working(): void;
}
interface Woman {
shopping(): void;
}
type Person = Man | Woman
const zhangSan: Person = {
working() {
console.log('working')
}
}
zhangSan.working()
const xiaoLi: Person = {
shopping() {
console.log('shopping')
}
}
xiaoLi.shopping()
但需要注意的是下面這種寫(xiě)法:
const person: Person = {
working() {
console.log('working')
},
shopping() {
console.log('shopping')
}
}
這樣寫(xiě)是不會(huì)報(bào)錯(cuò)的,但是當(dāng)試圖調(diào)用方法的時(shí)候就會(huì)報(bào)錯(cuò)欲逃,可以看出調(diào)用shopping
的時(shí)候找蜜,就會(huì)提示Man
不存在shopping
屬性,其實(shí)這是因?yàn)?code>A|B聯(lián)合類型表示的是A
或者B
中的任意一個(gè)類型稳析,只有一個(gè)類型洗做,而不是兩種類型的合并弓叛,下面來(lái)說(shuō)說(shuō)真正的合并交叉類型(&)
交叉類型(&)
A&B
高級(jí)類型是將兩個(gè)類型合并成了一個(gè)類型,這個(gè)類型擁有A
和B
的所有屬性诚纸,所以稱其為合并類型
也沒(méi)啥毛病邪码。這東西才學(xué)的時(shí)候一直以為是交集,其實(shí)應(yīng)該是并集才對(duì)咬清。
interface Apple {
size: string,
color: string,
}
interface Pen {
type: string,
color: string,
}
type Pineapple = Apple & Pen
const pineapple: Pineapple = {
size: '大',
color: '黃色',
type: '海南鳳梨'
}
這里有個(gè)問(wèn)題就是兩個(gè)Interface
做交叉類型時(shí)闭专,如果含有相同的key
會(huì)出現(xiàn)什么問(wèn)題呢?詳情請(qǐng)見(jiàn)另一篇旧烧。
類型索引(keyof)
keyof
的作用是將一個(gè)類型拆分開(kāi)了影钉,將拆分出來(lái)的子類型的集合作為類型返回,如下代碼掘剪,PersonKeys
的類型(圖keyof.png
所示)為name|age
這種聯(lián)合類型
interface Person {
name: string,
age: number
}
type PersonKeys = keyof Person
類型約束(extends)
extends
的作用是約束泛型平委,將泛型的類型限定成某個(gè)類型,如下例子:logPersonInfo<Person>()
可以傳入Man
和Woman
兩種類型夺谁,但logPersonInfo<Man>()
卻只能傳入Man
類型,logPersonInfo<Woman>()
卻只能傳入Woman
類型廉赔。
interface Person {
name: string,
age: number
}
class Man implements Person {
name = 'zhangSan';
age = 27;
working() {}
}
class Woman implements Person {
name = 'xiaoLi';
age = 26;
shopping() {}
}
function logPersonInfo<T extends Person>(t: T) {
console.log(`name : ${t.name} , age : ${t.age}`)
}
logPersonInfo<Person>(new Man())
logPersonInfo<Person>(new Woman())
logPersonInfo<Man>(new Man())
logPersonInfo<Woman>(new Woman())
下面再說(shuō)一種情況,來(lái)更加深入的了解extends
:
interface Person {
name: string,
age: number
}
class Alien {
name = "E.T"
age = 1000
fly() {
}
}
function logPersonInfo<T extends Person>(t: T) {
console.log(`name : ${t.name} , age : ${t.age}`)
}
logPersonInfo<Person>(new Alien())
這個(gè)時(shí)候我們傳入logPersonInfo<Person>()
的是一個(gè)Alien
匾鸥,這時(shí)候也不會(huì)有什么問(wèn)題蜡塌,因?yàn)?code>Alien包含了Person
的所有屬性。對(duì)比兩個(gè)例子我們能看得出來(lái)這里的extends
和類繼承的extends
是不一樣的勿负,這個(gè)地方的extends
起到的作用只是限制泛型T
的類型為包含了Person
類型的所有屬性(當(dāng)然所有屬性的類型也要一致)的類型馏艾。
類型映射(in)
in
高級(jí)類型起到的作用是做類型的映射,它會(huì)遍歷已有類型的所有key或者是聯(lián)合類型的所有類型奴愉,有點(diǎn)類似于forin
琅摩。下面我們寫(xiě)的demo,將已有類型的所有屬性轉(zhuǎn)換成可空類型锭硼。
interface Person {
name: string;
age: number;
}
//此時(shí)這里的P就是in遍歷出來(lái)的key
//將Person的所有key遍歷出來(lái)設(shè)置成?:可空類型房资,再賦值上key對(duì)應(yīng)的value
//這時(shí)候的PersonValueCanNull類型就是所有屬性可為空的Person類型了
type PersonValueCanNull = { [P in keyof Person]?: Person[P] }
如上代碼,這個(gè)時(shí)候PersonValueCanNull
的類型為{name?: string, age?: number}
,如圖:
再來(lái)個(gè)聯(lián)合類型的例子:
type ValueKeyType = "key1" | "key2" | "key3"
type ValueType = { [P in ValueKeyType]: boolean }
const value: ValueType = {
key1: false,
key2: false,
key3: false,
}
結(jié)合上面聯(lián)合類型的例子檀头,我們?cè)賮?lái)看第一個(gè)例子轰异,[P in keyof Person]
就可以拆分成[P in keyof "name"|"age"]
,其實(shí)最終都是使用聯(lián)合類型來(lái)進(jìn)行in
操作
條件類型(A ? B : C)
條件類型
其實(shí)就是一個(gè)三元運(yùn)算操作鳖擒,如果滿足條件A
那么就是B
類型溉浙,否則就是C
類型,話不多說(shuō)上代碼:
//傳入T和U蒋荚,如果T包含U所有的key,那么返回類型是T馆蠕,反之返回的是T和U的交叉類型
type MergeAction<T, U> = T extends U ? T : T & U
interface Teacher {
name: string,
age: number,
teachStudentNum: number
}
interface Father {
name: string,
age: number,
childNum: number
}
//回顧一下之前的 extends期升, Teacher里面沒(méi)有包含F(xiàn)ather里面的
//所有key惊奇,所以返回的是Teacher&Father的交叉類型
const person: MergeAction<Teacher, Father> = {
name: 'zl',
age: 27,
teachStudentNum: 60,
childNum: 1
}
類型如圖:
下面我們?cè)賮?lái)個(gè)keyof
的例子
const person :MergeAction<keyof Teacher, keyof Father> = "name"
此時(shí)的類型就是 name|age
的聯(lián)合類型
了。