这是我参与「第四届青训营 」笔记创作活动的第5天
目录简介
- Typescript 见解
- Typescript 基础语法,包括类型、泛型、类型别名、类型字面量等
- Typescript 高级类型讲解及实例
- Typescript 工程应用介绍
1.背景
1.1 发展历史
- 2012-10:微软发布了 TypeScript 第一个版本(0.8)
- 2014-10:Angular发布了基于 TypeScript 的 2.0 版本
- 2015-04:微软发布了 Visual Studio Code
- 2016-05: @types/react 发布,TypeScript 可开发 React
- 2020-09:Vue 发布了 3.0 版本,官方支持 TypeScript
- 2021-11:v4.5版本发布
总结:
- 到目前为止,三大前端框架均已支持TS
- Visual Studio Code 是最适合进行TS开发的软件
1.2 为何我们需要TS
| 语言 | 动静 | 强弱 |
|---|---|---|
| JavaScript | 动态类型 | 弱类型 |
| TypeScript | 静态类型 | 弱类型 |
-
静态类型:编译阶段类型检查,编译器负责。
-
动态类型:运行时类型检查,程序员负责。
-
弱类型:数字和字符串是可以相加的,因为背后执行了类型转换
-
强类型:数字和字符串是不可以相加的
静态类型
- 可读性增强: 基于语法解析TSDoc,ide增强,便于生成文档
- 可维护性增强: 在编译阶段暴露大部分错误
- 多人合作的大型项目中,获得更好的稳定性和开发效率
JS的超集
- 包含于兼容所有Js特性,支持共存
- 支持渐进式引入与升级
2.基本语法
2.1 基础数据类型
JS
TS
2.2 对象类型
2.3 函数类型
2.4 函数重载
根据传入的type参数的值来决定返回值的类型
函数重载是指在同一个作用域内,有多个函数名相同,但是形参列表不同(参数类型不同,参数个数不同,参数顺序不同),返回值无关,我们将这种叫做重载函数。重载的函数是通过形参列表区分的,和其他无关。一句话来说“一个接口,多种实现”,不仅函数可以重载,运算符也可以重载。
例如:如果要实现一个加法运算,加法函数可以是整型也可以是浮点型,就可以使用函数重载实现。 这样实现后,我们进行加法运算,则只需要调用ADD函数,编译器会根据我们传递实参的类型和个数,顺序判断想要调用那个ADD函数。
2.5 数组类型
数组是特殊的对象
Record<Keys, Type>
Constructs an object type whose property keys are Keys and whose property values are Type. This utility can be used to map the properties of a type to another type.
interface CatInfo {
age: number;
breed: string;
}
type CatName = "miffy" | "boris" | "mordred";
const cats: Record<CatName, CatInfo> = {
miffy: { age: 10, breed: "Persian" },
boris: { age: 5, breed: "Maine Coon" },
mordred: { age: 16, breed: "British Shorthair" },
};
2.6 typescript补充类型
2.7 typescript泛型
2.8 类型别名与类型断言
把类型全部写出来是很复杂的,用type关键字来命名它
2.9 字符串、数字 字面量
3.高级类型
3.1 联合/交叉类型
为书籍列表编写类型
const bookList = [{
author: 'xiaoming',
type: 'history',
range: '2001-2021',
},{
author: 'xiaoli',
type: 'story',
theme: 'love',
}]
2个对象的类型其实有点重复了 而且type的类型其实不是string,其实是只能在“love”或者“history”中二选一
interface IHistoryBook {
author: string;
type: string;
range: string
}
interface IStoryBook {
author: string;
type: string;
theme: string;
}
type lBookList = Array<IHistoryBook | IStoryBook>;
- 联合类型:IA|IB;联合类型表示一个值可以是几种类型之一
- 交叉类型:IA &IB;多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性
type lBookList = Array<{
author: string;
}&({
type: 'history';
range: string;
}|{
type: 'story';
theme: string;
})>
3.2 类型保护与类型守卫
interface lA { a: 1, a1: 2 }
interface lB { b: 1, b1: 2 }
function log(arg: IA | IB) {
/*报错: 类型“IA|IB”上不存在属性“a”。类型“IB”上不存在属性“a”。 */
/*结论: 访问联合类型时,处于程序安全,仅能访问联合类型中的交集部分*/
if (arg.a) {
console.log(arg.a1)
} else {
console.log(arg.b1);
}
}
访问联合类型时,只能访问共有的属性和方法,比如这里可以访问toString等对象都有的方法
所以本质上是把前一种方法的是非判断改进了一下
注意这里的!!的用法
!可将变量转换成布尔类型,对null、undefined和空字符串取反后均为true,对于非空字符串或非null对象,执行!运算后则为false,例如:
!null = true;
!undefined = true;
!'' = true;
!'wang' = false;
!!其实就是取非后再取非,常常用来做非空判断。!!常常可以简化代码,例如需要判断变量val为非null,未定义或者非空串才能执行方法体
var val;
if(val!=null&&typeof(val)!=undefined&&val!=''){
console.log('执行...');
}
利用!!可以写的非常简洁
if(!!val){
console.log('执行...');
}
类型保护
// 实现函数reverse
// 其可将数组或字符串进行反转
function reverse(target: string | Array<any>) {
/* typof 类型保护 */
if (typeof target == = 'string') {
return target.split('').reverse().join('");
}
/* instance 类型保护 */
if (target instanceof Object) {
return target.reverse();
}
}
这里在使用typeof或者instanceof时,并没有进行类型守卫也没有报错,因为TS智能识别了这2个词,进行了类型保护
type lBookList = Array<{
author: string;
}&({
type: 'history';
range: string;
}|{
type: 'story';
theme: string;
})>
// 实现函数logBook类型
// 函数接受书本类型,并logger出相关特征
function logBook(book: IBookltem) {
// 联合类型 +类型保护 =自动类型推断
if (book.type === 'history') {
console.log(book.range)
} else {
console.log(book.theme
);
3.3 高级类型
/**
实现merge函数类型
要求sourceObj必须为targetObj的子集
*/
function merge1(sourceObj, targetObj) {
const result = {...sourceObj};
for(let key in targetObj) {
const itemVal = sourceObj[key];
itemVal &&(result[key] = itemVal );//注意这里&&的用法
}
return result;
}
function merge2(sourceObj, targetObj) {
return { .….sourceObj, …targetObj};
}
Logical AND (&&)
分为3个层次来理解
1.执行布尔 AND 运算
当与布尔操作数一起使用时,&& 对两个值执行布尔 AND 运算:当且仅当它的第一个操作数和它的第二个操作数都为真时,它才返回真。 如果这些操作数中的一个或两个为假,则返回假。
&& 通常用作连接两个关系表达式的连词
x === 0 && y === 0 // true if, and only if, x and y are both 0
关系表达式总是计算为真或假,因此当这样使用时, && 运算符本身返回真或假。
关系运算符的优先级高于 &&(和 ||),因此可以安全地编写此类表达式而无需括号。
2.作为真值和假值的布尔 AND 运算符
&&不要求它的操作数是布尔值。- 所有 JavaScript 值要么是“真实的truthy”,要么是“虚假的falsy”。
- 在 JavaScript 中,任何需要布尔值的表达式或语句都可以接受真值或假值
如果两个操作数都是真值,则运算符返回一个真值returns a truthy value。
否则,一个或两个操作数必然是假的,并且运算符返回一个假值returns a falsy value。
3.捷径
该运算符首先计算其第一个操作数,即其左侧的表达式。
如果左边的值是假的falsy,那么整个表达式的值也一定是假的,所以&&只是简单地返回左边的值,甚至不计算右边的表达式。
另一方面,如果左边的值是真值,那么表达式的整体值取决于右边的值。如果右边的值是真实的,那么整体的价值一定是真实的, 如果右边的值是假的,那么整体的价值一定是假的。
所以当左边的值是真的时,&& 运算符计算并返回右边的值
let o = {x: 1};
let p = null;
o && o.x // => 1: o is truthy, so return value of o.x **返回的不是truthy或者falsy,返回的就是本来的值**
p && p.x // => null: p is falsy, so return it and don't evaluate p
以下两行代码等效
if (a === b) stop(); // Invoke stop() only if a === b
(a === b) && stop(); // This does the same thing
通常,当您在 && 的右侧编写带有副作用(赋值、递增、递减或函数调用)的表达式时,您必须小心。
这些副作用是否发生取决于左侧的值。
interface ISourceObj { x?: string; y?: string;
interface lTargetObj { x: string; y: string
type lMerge = (sourceObj: ISourceObj, targetObj: ITargetObj) = > ITargetObj;
/**
类型实现繁琐: 若obj类型较为复杂,则声明source和target便需要大量重复2遍*容易出错: 若target增加/减少key,则需要source联动去除*/
在定义时是不明确的,在使用时才会明确,我们也不知道函数的使用人会传入一个怎样的对象
未完待续