为了方便 TypeScript 用户,TypeScript 开发团队为我们提供了许多内置实用类型( Utility Types)。
今天我将用一个故事带大家了解几个开发中常用的内置类型。
出生
小明出生了,他的名字就叫小明,虽然很大众,但是父母并不打算为他改名字。
那么我们如何命名一个小明类型,并且限定好他的固定属性呢?
我们可以使用ReadOnly。
ReadOnly
使用语法:type readOnlyA = Readonly<A> 作用:得到一个和A类型属性相同但是全部只读的新类型
type Person = {
name: string
gender: number
}
type XiaoMing = Readonly<Person>
/* 等价于:
type XiaoMing = {
readonly name: string;
readonly gender: number;
} */
这时,通过使用ReadOnly包裹Person类型得到的XiaoMing类型的各个属性都是readonly的,即初始化之后不可更改。
让我们实例化一个小明吧:
let xiaoMing:XiaoMing = {
name: 'xiaoMing',
gender: 1
}
// xiaoMing.age = 1 // 语法报错: 无法分配到 "age" ,因为它是只读属性。
礼物
时间一转眼,小明已经四岁,小明爸想给孩子买一些礼物,于是他来到了超市,看到了以下物品:
type Things = {
辣条: string,
奥特曼变身器: string,
学习用品: string
}
const things:Things = {
辣条: '123',
奥特曼变身器: '迪迦神光棒',
学习用品: '全套三角尺'
}
但是小明爸不知道孩子喜欢什么,超市管理员和小明爸很熟,说可以先全部拿回去,小明不喜欢的再退回来。
于是,我们需要一个变量包含上面类型的属性,但同时每一个属性又都是可选的。此时我们可以使用partial。
Partial
使用语法:type PartialA = Partial<A> 将类型定义的所有属性都修改为可选。
type ChoiceThings = Partial<Things>
/* 等价于:
type ChoiceThings = {
辣条?: string | undefined;
奥特曼变身器?: string | undefined;
学习用品?: string | undefined;
} */
小明见到了礼物,果断的选择了两样:
const myThings:ChoiceThings = {
辣条: '123',
奥特曼变身器: '迪迦神光棒'
} // 由于所有的属性都是可选的,所以我们可以只需要其中几个属性
兴趣班
小明的妈妈想给孩子报兴趣班,于是他给了小明一个课表,让小明自己选择:
type Lesson = {
english : number //number代表星期几
dance ?: number
draw ?: number
}
但是小明全都感兴趣,他想构造一个自己的课表类型,这个类型需要每一个属性。
所以Requied就派上了用场:
Rquied
用法:Readonly<Type> 构造一个类型,该类型由设置为 Type 的所有属性组成。
type MyLesson = Required<Lesson>
/* 等价于:
type MyLesson = {
english: number;
dance: number;
draw: number;
} */
小明使用自己的课表类型初始化了一个课程安排:
const myLesson: MyLesson = {
english: 1,
dance: 3,
draw: 5
}
朋友
在兴趣班里,小明交到了Timor,Lulu, Fizz三个好朋友,他们都是友好的约德尔人。
type Frind = 'timor' | 'lulu' | 'fizz'
现在,小明想做一个朋友簿,记录每一个朋友的信息。但是懒惰的程序员不想再去写一个朋友薄类型了,我们想利用已有的类型来确定。此时我们可以用到Record。
Record
使用语法:type A = Record<Key, Type>构造一个对象类型,其属性键为Keys,属性值为Type。此实用程序可用于将一种类型的属性映射到另一种类型。
type Frinds = Record<Frind, Person> // Person类型在文章开头声明过
/* 等价于:
type Frinds = {
timor: Person;
lulu: Person;
fizz: Person;
} */
于是小明就可以利用该类型声明一个朋友薄:
const frinds:Frinds = {
timor: {
name: 'timor',
gender: 1
},
lulu: {
name: 'lulu',
gender: 0
},
fizz: {
name: 'fizz',
gender: 1
}
}
新课表
小明最近时间有些紧张,随着对兴趣课的深入,他需要在下面的全部课程中做出选择:
type LessonDetail = {
lessonName: string // 课程名
day: number // 星期几上课
timeConsumed: number // 课程消耗时间
}
type AllLesson = Record<'english' | 'dance' | 'draw', LessonDetail> // 这里使用到了Record
一番抉择下,小明放弃了成为舞蹈巨星的道路。现在他需要一个新的课程类型,用来制作他的新课表。
Pick
用法: Pick<Type, Keys> 通过从Type中选取一组属性Keys(字符串文字或字符串文字的并集)来构造一个新类型。
type NewLesson = Pick<AllLesson, 'english' | 'draw'>
/* 等价于:
type NewLesson = {
english: LessonDetail;
draw: LessonDetail;
} */
Omit
和Pick正好相反,Omit通过从Type中选取所有属性Type然后删除Keys(字符串文字或字符串文字的并集)来构造类型。
用法: Omit<Type, Keys>
所以我们也可以使用Omit来声明新类型:
type NewLesson = Omit<AllLesson, 'dance'>
// 等价于 type NewLesson = Pick<AllLesson, 'english' | 'draw'>
小明使用NewLesson声明了新的课表:
const newLesson: NewLesson = {
english: {
lessonName: 'verb',
day: 1,
timeConsumed: 2
},
draw: {
lessonName: 'egg',
day: 5,
timeConsumed: 3
}
}
Exclude
回到开始的问题,我们的新课表类型的构造也可以使用Exclude来进行:
用法:Exclude<UnionType, ExcludedMembers>
Exclude通过从联合类型UnionType中排除ExcludedMembers联合类型的所有成员来构造类型。
所以,我们的NewLesson属性也可以这样构造:
type NewLesson = Record<Exclude<'english' | 'dance' | 'draw', 'dance'>, LessonDetail>
// 等价于 Record<Exclude<'english' | 'draw'>, LessonDetail>
当然,这样做有点自己给自己添麻烦了。这里只是为了演示Exclude的作用才这么写。
有趣的函数
这天,Timor拿来了一个函数:
// Timor是这么写的
function stringToSum(a: string, b: string):Reason {
const res = parseInt(a) + parseInt(b)
return {
res,
isOk: !isNaN(res)
}
}
小明也想使用一下该函数,所以他使用了Parameters, ReturnType 来定义函数的请求参数和返回值的类型。
Parameters
用法:Parameters<typeof fnName> 从函数类型 Type 的参数中使用的类型构造元组类型。
let args: Parameters<typeof stringToSum> // 相当于 let args: [a: string, b: string]
ReturnType
用法:ReturnType<typeof fnName> 构造一个由函数 Type 的返回类型组成的类型。
let res: ReturnType<typeof stringToSum> // 相当于 let res: Reason
小明利用了上面声明的变量来使用Timor的函数:
args = ['12', '34']
res = stringToSum(...args)
总结
到这里,我们已经介绍了9种常用的TypeScript程序类型,他们分别是:
ReadOnly<Type>得到一个和Type类型属性相同但是全部只读的新类型Partial<Type>将Type类型定义的所有属性都修改为可选。Rquied<Type>构造一个类型,该类型由设置为 Type 的所有属性组成。Record<Key, Type>构造一个对象类型,其属性键为Keys,属性值为Type。Pick<Type, Keys>从Type中选取一组属性Keys来构造一个新类型。Omit<Type, Keys>从Type中选取所有属性Type然后删除Keys来构造类型。Exclude<UnionType, ExcludedMembers>从联合类型UnionType中排除ExcludedMembers联合类型的所有成员来构造类型。Parameters<typeof fnName>从函数类型 Type 的参数中使用的类型构造元组类型。ReturnType<typeof fnName>构造一个由函数 Type 的返回类型组成的类型。
说完了常用程序类型,小明的故事也先就先告一段落啦。以后如果在面试或者职场中,有人向你提起程序类型,你就可以清清嗓子,然后说:”咳咳,那我就要给你讲一个小明的故事了...”
ts系列文章
更实用的TypeScript指南:不墨迹的入门 - 掘金 (juejin.cn)