TypeScript 高级类型

前言


之前一直使用的是 js 现在转过来学习 ts 的时候尝到了 ts 对类型规范的很多好处,相应的 ts 的类型有时候也让人头大,下面简单总结一下自己对 ts 高级类型的学习成果。

一般类型


//给变量指定类型
const value: string = "value"
//给方法的形参和返回值指定类型
const fun0 = (str: string): string => {
    return ""
}
//甚至可以直接给方法指定类型
type funType = (str: string) => string
const fun1: funType = (str) => {
    return ""
}

这个时候我们可以看的 fun1 这时候是不用指定 str 的类型的,IDE会自动提示,如下:

image

但还有个特殊的情况 fun1 的形参是可以不写的,比如:

type funType = (str: string) => string
const fun2: funType = () => {
    return "fun2 return value"
}
console.log(fun2("value"));

这样是不会报错的,可是为什么呢?我们明明指定了funType的形参有且是string,那大家肯定想问真是为什么,抱歉我也不知道,哈哈哈!如果有知道的朋友希望能留言告知。这样做的问题是在fun2里面是拿不到传入的形参的,不知道是不是bug。

泛型


对就是它,我个人来说在学习和使用ts的途中最头疼的就是泛型,ts中泛型的使用真的让人头晕眼花,但当你真的看明白ts中泛型的使用是又不得不说这简直就像是在变魔术。下面记录一下泛型的一般用法:

//将对象中值为number类型的数据组成一个新对象
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));

可以看到,当我们调用getObjAllNum的时候T类型已经转变成对象value的类型了。

image.png

高级类型


接下来要说的就是ts为我们提供的一些类型的高级操作方式

类型别名(type)

可以理解成可以给一个类型再取另一个名字,比如:

type myString = string
const str: myString = "str"

联合类型(|)

A|B表示A或者B中的任意一个类型 ,如下代码,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()

但需要注意的是下面这种写法:

const person: Person = {
    working() {
        console.log('working')
    },
    shopping() {
        console.log('shopping')
    }
}

这样写是不会报错的,但是当试图调用方法的时候就会报错,可以看出调用shopping的时候,就会提示Man不存在shopping属性,其实这是因为A|B联合类型表示的是A或者B中的任意一个类型,只有一个类型,而不是两种类型的合并,下面来说说真正的合并交叉类型(&)

image.png

交叉类型(&)

A&B高级类型是将两个类型合并成了一个类型,这个类型拥有AB的所有属性,所以称其为合并类型也没啥毛病。这东西才学的时候一直以为是交集,其实应该是并集才对。

interface Apple {
    size: string,
    color: string,
}
interface Pen {
    type: string,
    color: string,
}
type Pineapple = Apple & Pen
const pineapple: Pineapple = {
    size: '大',
    color: '黄色',
    type: '海南凤梨'
}

这里有个问题就是两个Interface做交叉类型时,如果含有相同的key会出现什么问题呢?详情请见另一篇

类型索引(keyof)

keyof的作用是将一个类型拆分开了,将拆分出来的子类型的集合作为类型返回,如下代码,PersonKeys的类型(图keyof.png所示)为name|age这种联合类型

interface Person {
    name: string,
    age: number
}
type PersonKeys = keyof Person
keyof.png

类型约束(extends)

extends的作用是约束泛型,将泛型的类型限定成某个类型,如下例子:logPersonInfo<Person>()可以传入ManWoman两种类型,但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())

下面再说一种情况,来更加深入的了解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())

这个时候我们传入logPersonInfo<Person>()的是一个Alien,这时候也不会有什么问题,因为Alien包含了Person的所有属性。对比两个例子我们能看得出来这里的extends和类继承的extends是不一样的,这个地方的extends起到的作用只是限制泛型T的类型为包含了Person类型的所有属性(当然所有属性的类型也要一致)的类型。

类型映射(in)

in高级类型起到的作用是做类型的映射,它会遍历已有类型的所有key或者是联合类型的所有类型,有点类似于forin。下面我们写的demo,将已有类型的所有属性转换成可空类型。

interface Person {
    name: string;
    age: number;
}
//此时这里的P就是in遍历出来的key
//将Person的所有key遍历出来设置成?:可空类型,再赋值上key对应的value
//这时候的PersonValueCanNull类型就是所有属性可为空的Person类型了
type PersonValueCanNull = { [P in keyof Person]?: Person[P] }

如上代码,这个时候PersonValueCanNull的类型为{name?: string, age?: number},如图:

image.png

再来个联合类型的例子:

type ValueKeyType = "key1" | "key2" | "key3"
type ValueType = { [P in ValueKeyType]: boolean }
const value: ValueType = {
    key1: false,
    key2: false,
    key3: false,
}

结合上面联合类型的例子,我们再来看第一个例子,[P in keyof Person]就可以拆分成[P in keyof "name"|"age"],其实最终都是使用联合类型来进行in操作

条件类型(A ? B : C)

条件类型其实就是一个三元运算操作,如果满足条件A那么就是B类型,否则就是C类型,话不多说上代码:

//传入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里面没有包含Father里面的
//所有key,所以返回的是Teacher&Father的交叉类型
const person: MergeAction<Teacher, Father> = {
    name: 'zl',
    age: 27,
    teachStudentNum: 60,
    childNum: 1
}

类型如图:

image.png

下面我们再来个keyof的例子

const person :MergeAction<keyof Teacher, keyof Father> = "name"

此时的类型就是 name|age联合类型了。

image.png

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355

推荐阅读更多精彩内容

  • 在前端开发中,大家入门ts其实是很简单的,只要掌握一些基本的类型知识就可以逐步的将js过渡到ts的应用,然而,当我...
    mm_tang阅读 1,577评论 0 1
  • 映射类型 一个常见的任务是将一个已知的类型每个属性都变为可选的: interface PersonPartial ...
    2o壹9阅读 1,530评论 1 49
  • 一、交叉类型 交叉类型将多个类型合并为一个新的类型,新的具有所有参与合并的类型的特性,本质上是一种并的操作。形式如...
    kuxingseng686阅读 532评论 0 0
  • 交叉类型 交叉类型将多个类型合并为一个类型,相当于新类型具有这多个类型的所有特性,相当于是一种并的操作,通常在使用...
    这个前端不太冷阅读 667评论 0 0
  • 多态的 this类型 多态的 this类型表示的是某个包含类或接口的 子类型。 这被称做 F-bounded多态性...
    2o壹9阅读 521评论 1 50