共计 6049 个字符,预计需要花费 16 分钟才能阅读完成。
写作背景:
承接上一篇扒官网文档学 Ts 类型编程来持续扒完类型编程的后两个章节 Mapped Types 和 Template Literal Types,同样筹备了演练场的代码能够同步调试察看输入的类型来学习。
TypeScript 类型操作:
TypeScript 类型零碎的弱小之处次要体现在它容许咱们通过类型来表白类型,也就是说咱们能够通过现有的类型通过一系列的操作失去另一个类型(从类型创立类型),咱们将通过上面表格所列举的程序来解说如何表白一个新的类型:
序号 | Types | 类型 | 形容 |
---|---|---|---|
1 | Generics-Types | 泛型类型 | 带参数的类型 |
2 | Keyof Type Operator | Keyof 类型运算符 | 应用 keyof 运算符创立新类型 |
3 | Typeof Type Operator | Typeof 类型运算符 | 应用 typeof 运算符创立新类型 |
4 | Indexed Access Types | 索引拜访类型 | 应用 Type[‘a’]语法拜访类型的子集 |
5 | Conditional Types | 条件类型 | 行为相似于类型零碎中的 if 语句的类型 |
6 | Mapped Types | 映射类型 | 通过映射现有类型中的每个属性来创立类型 |
7 | Template Literal Types | 模板字符串类型 | 通过模板字符串更改属性的映射类型 |
Mapped Types:
通过应用映射类型能够再不从新定义的前提下创立另一种新的类型,映射类型也是一种通用类型,接下来咱们通过一系列的示例来感受一下映射类型的应用。
正式开始前须要明确以下 4 点:
- 应用映射类型的最根底是通过索引类型拜访来实现的;
- 应用映射类型前应该有一个类型;
- 应用 keyof 关键字来失去输出类型中 key 组成的联结类型;
- 应用 in 关键字能够遍历联结类型。
入门案例:HelloWorld
理解一个最简略的映射类型工具的应用。
上面这块代码是咱们待输出的类型:
type FeatureFlags = {darkMode: () => void;
newUserProfile: () => void;};
类型工具阐明:
借助上面的通用映射类型工具,将输出类型 Type 的 key 来作为新对象类型的 key,value 的类型对立为 boolean 类型进行束缚。
type OptionsFlags<Type> = {[Property in keyof Type]: boolean;
};
咱们应用一下这个类型工具:
type FeatureOptions = OptionsFlags<FeatureFlags>;
// ^?
去演练场验证后果
案例 - 在映射类型中应用修饰符:
在映射类型中同样反对在 JavaScript 中对对象的润饰属性,readonly,属性可选。在映射类型中还能够对曾经存在的这些修饰符进行删除。
剔除输出类型中的 readonly 润饰
上面这块代码是咱们待输出的类型:
type LockedAccount = {
readonly id: string;
readonly name: string;
};
类型工具阐明:
借助上面的通用的映射类型工具,将输出类型中的 readonly 删除掉,失去一个所有属性均非只读的对象类型;
type CreateMutable<Type> = {-readonly [Property in keyof Type]: Type[Property];
};
咱们应用一下这个类型工具:
type UnlockedAccount = CreateMutable<LockedAccount>;
// ^?
去演练场验证答案
剔除输出类型中的可选润饰:
上面这块代码是咱们待输出的类型
type MaybeUser = {
id: string;
name?: string;
age?: number;
};
类型工具阐明:
借助上面的通用的映射类型工具,将输出类型中的可选修饰符删除掉,失去一个所有属性均必填的对象类型;
type Concrete<Type> = {[Property in keyof Type]-?: Type[Property];
};
咱们应用一下这个类型工具:
type User = Concrete<MaybeUser>;
// ^?
去演练场验证答案
案例 - 搭配模板字符类型应用
模板字符类型能够不便咱们链接 / 扩大为更多的字符串类型,应用类同咱们在 JavaScript 中模板字符串的应用,但模板类型作用在类型地位。
模块字符类型咱们会在上面独自讲,这个案例应用到的 Capitalize 会将传入的 Property 转为首字母大写。
上面这块代码是咱们待输出的类型
interface Person {
name: string;
age: number;
location: string;
}
类型工具阐明:
借助上面的通用的映射类型工具,咱们能够为输出的类型 Type 减少对应的已 get 为前缀的函数。咱们通常在定义完对象属性后会减少对应属性获取的函数而不是间接对外裸露这个属性。
type Getters<Type> = {[Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
};
咱们应用一下这个类型工具:
type LazyPerson = Getters<Person>;
// ^?
去演练场验证答案
案例 - 映射类型 + 内置的类型工具
应用内置的类型工具 Exclude 来配合映射类型剔除掉输出类型的指定属性后创立一个新的类型。
上面这块代码是咱们待输出的类型
interface Circle {
kind: "circle";
radius: number;
}
类型工具阐明:
借助上面的通用的映射类型工具,在应用索引类型形式 key 时应用 as 关键字来对 Property 进行进一步的解决,应用 Exclude 剔除掉所蕴含的 kind,从而失去一个新的类型。
type RemoveKindField<Type> = {[Property in keyof Type as Exclude<Property, "kind">]: Type[Property]
};
咱们应用一下这个类型工具:
type KindlessCircle = RemoveKindField<Circle>;
// ^?
去演练场验证答案
案例 - 映射联结类型
上面这块代码是咱们待输出的类型
type SquareEvent = {kind: "square", x: number, y: number};
type CircleEvent = {kind: "circle", radius: number};
类型工具阐明:
借助上面的通用的映射类型工具,在输出类型做了束缚,必须要蕴含 king 且类型为 string 的属性,在遍历 Events 的时候应用 as 取 E 中名为 kind 的 value 作为新类型的 key。
type EventConfig<Events extends {kind: string}> = {[E in Events as E["kind"]]: (event: E) => void;
}
咱们应用一下这个类型工具:
type Config = EventConfig<SquareEvent | CircleEvent>
// ^?
去演练场验证答案
Template Literal Types:
模板字符类型的语法同 JavaScript 中模板字符串,但应用的地位不同,模板字符类型利用在类型地位。通过应用模板类型来扩大 / 链接内容创立新的字符类型。
入门案例:HelloWorld
上面使咱们的入门案例,通过模板插值将类型 World 插入到了类型 Greeting 中,最终创立的类型为“hello world”,留神这外面的“hello world”是类型而不是值。
type World = "world";
type Greeting = `hello ${World}`;
// ^?
去演练场验证答案
案例 - 插值地位应用联结类型:
上面的案例能够失去一个后果,当插值地位是用联结类型是,后果将是由每个联结成员便是的每个可能的字符串组成的汇合。
type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
// ^?
去演练场验证答案
案例 - 多个插值地位应用联结类型:
上面的案例能够失去一个后果,当每个插值地位均应用联结类型时,后果的数量将是每个联结元素个数相乘的积。
type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
type Lang = "en" | "ja" | "pt";
type LocaleMessageIDs = `${Lang}_${AllLocaleIDs}`;
// ^?
去演练场验证答案
案例 - 在类型中应用字符串联结
案例介绍:
- 咱们通常会在做一系列操作的函数后面定义一个显著的前缀;
- 在这个案例中实现监听对象属性的变动;
- 须要有一个 on 函数来接管,监听事件的名称咱们做特定的束缚格局为:key+Changed;callback 是一个没有返回值的函数。
type PropEventSource<Type> = {on(eventName: `${string & keyof Type}Changed`, callback: (newValue: any) => void): void;
};
/// Create a "watched object" with an 'on' method
/// so that you can watch for changes to properties.
declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>;
const person = makeWatchedObject({
firstName: "Saoirse",
lastName: "Ronan",
age: 26
});
person.on("firstNameChanged", () => {});
// Prevent easy human error (using the key instead of the event name)
person.on("firstName", () => {});
// It's typo-resistant
person.on("frstNameChanged", () => {});
去演练场验证答案
案例 - 模板字符类型推导
这个案例是上一个案例的降级版本,减少了对返回类型的推导,使得这个类型工具将更加的欠缺。
type PropEventSource<Type> = {
on<Key extends string & keyof Type>
(eventName: `${Key}Changed`, callback: (newValue: Type[Key]) => void): void;
};
declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>;
const person = makeWatchedObject({
firstName: "Saoirse",
lastName: "Ronan",
age: 26
});
person.on("firstNameChanged", newName => {console.log(`new name is ${newName.toUpperCase()}`);
});
person.on("ageChanged", newAge => {if (newAge < 0) {console.warn("warning! negative age");
}
})
去演练场验证答案
整顿内置的模板字符操作类型:
将每个字符均转为大写:
type Greeting = "Hello, world"
type ShoutyGreeting = Uppercase<Greeting>
// ^?
type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`
type MainID = ASCIICacheKey<"my_app">
// ^?
去演练场验证答案
将每个字符均转为小写:
type Greeting = "Hello, world"
type QuietGreeting = Lowercase<Greeting>
// ^?
type ASCIICacheKey<Str extends string> = `id-${Lowercase<Str>}`
type MainID = ASCIICacheKey<"MY_APP">
// ^?
去演练场验证答案
将第一个字符转为大写
type LowercaseGreeting = "hello, world";
type Greeting = Capitalize<LowercaseGreeting>;
// ^?
去演练场验证答案
将第一个字符转为小写:
type UppercaseGreeting = "HELLO WORLD";
type UncomfortableGreeting = Uncapitalize<UppercaseGreeting>;
// ^?
去演练场验证答案
写到最初:
至此 TypeScript 类型编程的 7 大块内容就曾经过了一遍了,模板字符类型的案例还须要多相熟相熟。在官网还有一些提供的内容类型工具能够间接供咱们在理论开发中应用,这里给出 Utility Types 的地址不便大家查问。类型编程和咱们以往的编程一样,同样在乎根底的学习和大量的练习。上次举荐的开源类型挑战我的项目 type-challenges 你有练习打卡吗?
因为平台对外链限度,倡议拜访原文取得更好的浏览体验,文中的大量案例沿用了官网文档的示例,同样能够参考官网文档,不足之处还请斧正。
团队介绍
高灯科技交易合规前端团队(GFE), 隶属于高灯科技 (北京) 交易合规业务事业线研发部,是一个富裕激情、充斥创造力、保持技术驱动全面成长的团队, 团队平均年龄 27 岁,有在各自畛域深耕多年的大牛, 也有刚刚毕业的小牛, 咱们在工程化、编码品质、性能监控、微服务、交互体验等方向踊跃进行摸索, 谋求技术驱动产品落地的主旨,打造欠缺的前端技术体系。
- 愿景: 成为最值得信赖、最有影响力的前端团队
- 使命: 保持客户体验第一, 为业务发明更多可能性
- 文化: 敢于承当、深刻业务、集思广益、简略凋谢
Github:github.com/gfe-team
团队邮箱:gfe@goldentec.com
作者:GFE- 小鑫同学
著作权归作者所有。商业转载请分割作者取得受权,非商业转载请注明出处。