这是我参与「第五届青训营 」伴学笔记创作活动的第 4 天
一、本堂课重点内容:
- TypeScript 历史及定义解析
- TypeScript 基础语法
- TypeScript 高级类型
- TypeScript 工程应用
二、详细知识点介绍:
2.1 TypeScript 历史及定义解析
2.1.1 Typescript发展史
- 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版本发布
2.2 为什么是TypeScript
- 静态类型
- 可读性强:基于语法解析TSDoc,ide增强
- 可维护性增强:在编译阶段暴露大部分错误
- 多人合作的大型项目中,获得更好的稳定性和开发效率
- JS的超集
- 包含于兼容所有Js特性,支持共存
- 支持渐进式引入与升级
2.2 TypeScript 基础语法
2.2.1 基础数据类型
可以将 TS 中的常用基础类型分为两类
- JavaScript 已有类型
- 原始类型:
number/string/boolean/null/undefined/symbol - 对象类型:
object(数组、对象、函数等)
/* 字符串 */
const q:string = 'string'
/* 数字 */
const w: number = 1;
/* 布尔值 */
const e:boolean =true
/* null */
const r:null = null
/* undefined */
const t:undefined = undefined
2.2.2 对象类型
写法:
const bytedancer:IBytedancer = {
jobId: 124431,
name: 'lin',
sex: 'man',
age: '18',
hobby: 'football'
}
interface IBytedancer {
/* 只读属性:约束属性不可在对象初始化外赋值 */
readonly jobId: number
name: string
sex: 'man' | 'woman' | 'other'
age: number
/*可选属性:定义该属性可以不存在*/
hobby?:string
/*任意属性:约束所有对象属性都必须是该属性的子类型*/
[key: string]: any
}
2.2.3 函数类型
除了变量,我们常见的类型指定还有针对函数的类型声明
函数类型需要指的是 函数参数和返回值的类型,这里分为两种写法
- 第一种: 单独指定参数,返回值类型
// 单独指定函数返回值和函数参数
function add(num1: number, num2: number): number {
return num1 + num2;
}
// 指定变量形式的
const add2 = (num1: number, num2: number): number => {
return num1 + num2;
};
- 第二种, 同时指定参数和返回值
// 同时指定参数和返回值
type CustomFunc = (num1: number, num2: number) => number;
const add3: CustomFunc = (num1, num2) => {
return num1 + num2;
};
注意: 当函数作为表达式时,可以通过类似箭头函数形式的语法来为函数添加类型,这种形式只适用于函数表达式
2.2.3.1 void 类型
当我们的函数定义为没有返回值的类型时,可用关键字void表示
// 没有返回值的函数
type CustomFunc1 = (num1: string, num2: number) => void;
const combinStr: CustomFunc1 = () => {};
如果一个函数没有返回值,此时,在 TS 的类型中,应该使用 void 类型
const add4 = () => {};
// 如果什么都不写 表示add4函数的类型为void
const add5 = (): void => {};
// 这种写法明确指定返回值为void与上方的类型相同
const add6 = (): undefined => {
return undefined;
};
// 如果指定返回值为undefined return undefined
2.2.3.2 函数可选参数
当我们定义函数时,有的参数可传可不传,这种情况下,可以使用 TS 的可选参数来指定类型
比如,在使用数组的slice方法时,我们可以直接使用slice() 也可以传入参数 slice(1) 也可以slice(1,3)
const slice = (start?: number, end?: number): void => {};
? 表示该参数或者变量可传可不传
注意:可选参数只能出现在参数列表的最后, 即必须参数必须在可选参数之前
2.2.3.3 函数重载
2.2.4 数组类型
数组两种写法
类型[]写法, 如
let userList: string[] = ['John', 'Bob', 'Tony'];
let peopleList: object[] = [{ name: '张三', age: 18 }];
- Array<类型>写法, 如
let user2List: Array<string> = ['John', 'Bob', 'Tony'];
let people2List: Array<object> = [{ name: '张三', age: 18 }];
2.2.5 TypeScript补充类型
/*空类型,表示无赋值*/
type IEmptyFunction = ()=> void;
/*任意类型,是所有类型的子类型*/
type IAnyType = any;
/* 枚举类型:支持枚举值到枚举名的正、反向映射*/
enum EnumExample {
add ='+',
mult = '*',
}
EnumExample ['add'] === '+';
EnumExample['+'] === 'add' ;
enum ECorlor { Mon,Tue,wed, Thu,Fri,Sat,Sun };
ECorlor ['Mon'] === 0;
ECorlor[0] 'Mon';
/*泛型*/
type INumArr = Array<number>;
2.2.6 TypeScript范型
2.2.7 字符串/数字 字面量
/*允许指定字符串/数字必须的固定值*/
/*IDomTag必须为html、body、div、span中的其一*/
type IDomTag = 'html' | 'body' | 'div' | 'span';
/* IOddNumber必须为1、3、5、7、9中的其一*/
type IOddNumber = 1 | 3 | 5 | 7 | 9;
2.3 TypeScript 高级类型
2.3.1 联合交叉类型
- 联合类型:IA|IB;联合类型表示一个值可以是几种类型之一
- 交叉类型:IA & IB;多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性
示例:
const oookList =[ {
author:'xiaoming',
type:'history',
range :'2001一2021'
}, {
author :'xiaoli',
type: 'story',
theme: 'love'
}]
type IBookList = Array<{
author:string ;
} & ({
type: 'history';
range: string;
} | {
type: 'story';
theme: string;
})>
2.3.2 类型保护与类型守卫
interface IA {a: 1, al: 2 }
interface IB{ b: 1, bl:2 }
/类型卫: 定义一个函数,它的返回值是一个类型谓词,生效范围为子作用域*/
function getIsIA (arg: IA | IB): arg is IA {
return !! (arg as IA).a;
}
function log2 (arg: IA | IB){
if(getIsIA(arg)) {
console.log (arg.al)
} else {
console.log (arg . b1);
}
}
2.4 TypeScript 工程应用
三、实践练习例子:
- 实现函数reverse
//函数接受书本类型,并logger出相关特征
function logBook (book: IBookItem){
//联合类型+类型保护=自动类型推断
if(book.type === 'history'){
console.log (book.range)
}else {
console.log (book.theme);
}
}
- 实现merge函数类型
/**
* 要求sourceObj必须为targetObj的子集
*/
function mergel (sourceObj, targetobj){
const result = {...sourceObj };
for (let key in target0bj){
const itemVal = sourceObj [ key] ;
itemval && ( result[key]= itemVal );
}
return result;
}
function merge2 (sourceObj, targetobj){
return {...sourceObj, ...targetobj };
}
interface ISourceObj {
x?: string;
y?: string;
}
interface ITargetObj {
x: string;
y: string;
}
type IMerge = (sourceObj : ISourceObj, targetObj: ITargetObj)
=> ITargetobj;
/**
*类型实现繁琐:若obj类型较为复杂,则声明source和target便需要大量重复2遍
*容易出错:若target增加/减少key,则需要source联动去除
*/
interface IMerge {
<T extends Record<string, any>> (source0bj: Partial<T,target0bj:T):T;
}
type IPartial<T extends Record<string,any>> = {
[P in keyof T]?:T[P];
}
// 索引类型:关键字【keyof】,其相当于取值对象中的所有key组成的字符串字面量,如
type IKeys = keyof { a: string; b: number };// => type IKeys = "a" | ".b"
//关键字【in】,其相当于取值字符串字面量中的一种可能,配合泛型P,即表示每个key
/关键字【?】,通过设定对象可选选项,即可自动推导出子集类型
- 函数返回值类型
//实现函数delayCall的类型声明
// delayCall接受一个函数作为入参,其实现延迟1s运行函数
// 其返回promise,结果为入参函数的返回结果
function delayCall (func){
return new Promise (resolve =>{
setTimeout (()=>{
const result = func ();
resolve (result);
}, 1000);
});
}
type IDelayCall = <T extends () =>any> (func: T) => ReturnType<T>;
type IReturnType<T extends (...args: any) =>any> = T extends (...args: any)
=> infer R ? R : any
//关键字【extends】跟随泛型出现时,表示类型推断,其表达可类比三元表达式
//如T===判断类型﹖类型A:类型B
// 关键字【infer】出现在类型推荐中,表示定义类型变量,可以用于指代类型
// 如该场景下,将函数的返回值类型作为变量,使用新泛型R表示,使用在类型推荐命中的结果中
四、课后个人总结:
-
TypeScript是一种由微软开发的超集JavaScript语言,具有静态类型检查和类型推导的能力。
-
TypeScript提供了丰富的高级类型,如接口、类、泛型等,使得开发大型项目更加稳定和高效。
-
TypeScript还支持JavaScript的所有特性,可以渐进式地引入和升级。
-
在学习 TypeScript 时,不容易掌握的知识点:
- 高级类型(如泛型、类型别名、联合类型等):这些类型在某些情况下可能需要更多的思考和理解,在实际应用中可能不是特别常用。
- 接口: TypeScript 中接口的使用可能比较麻烦,在某些情况下需要考虑多种使用场景。
- 类型守卫: 类型守卫可以帮助我们在运行时确定一个变量的类型,这可能对于初学者来说有些难以理解。
- 高级应用(如装饰器、混入、模块等): 这些高级应用在某些情况下可能需要更多的思考和理解。