TypeScript 入门 | 青训营笔记

76 阅读7分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 4 天

image.png

一、本堂课重点内容:

  1. TypeScript 历史及定义解析
  2. TypeScript 基础语法
  3. TypeScript 高级类型
  4. 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 中的常用基础类型分为两类

  1. 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 函数重载

image.png

2.2.4 数组类型

数组两种写法

  1. 类型[]写法, 如
let userList: string[] = ['John', 'Bob', 'Tony'];
let peopleList: object[] = [{ name: '张三', age: 18 }];
  1. Array<类型>写法, 如
let user2List: Array<string> = ['John', 'Bob', 'Tony'];
let people2List: Array<object> = [{ name: '张三', age: 18 }];

image.png

2.2.5 TypeScript补充类型

/*空类型,表示无赋值*/
type IEmptyFunction = ()=> void;
/*任意类型,是所有类型的子类型*/
type IAnyType = any;
/* 枚举类型:支持枚举值到枚举名的正、反向映射*/
enum EnumExample {
add ='+',
mult = '*',
}

EnumExample ['add'] === '+';
EnumExample['+'] === 'add' ;

enum ECorlor { Mon,Tue,wed, ThuFri,Sat,Sun };
ECorlor ['Mon'] === 0;
ECorlor[0] 'Mon';
/*泛型*/
type INumArr = Array<number>;

2.2.6 TypeScript范型

image.png

image.png

image.png

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 工程应用

image.png

image.png

三、实践练习例子:

  • 实现函数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 中接口的使用可能比较麻烦,在某些情况下需要考虑多种使用场景。
    • 类型守卫: 类型守卫可以帮助我们在运行时确定一个变量的类型,这可能对于初学者来说有些难以理解。
    • 高级应用(如装饰器、混入、模块等): 这些高级应用在某些情况下可能需要更多的思考和理解。