学习TypeScript | 青训营笔记

136 阅读8分钟

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

一、本堂课重点内容:

本堂课的知识要点有哪些?

  • 认识TypeScript
  • TypeScript基本语法
  • TypeScript高级类型
  • TypeScript工程应用

二、详细知识点介绍:

认识TypeScript

TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准。TypeScript 由微软开发的自由和开源的编程语言。TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。

TypeScript发展历史

2012-10:微软发布了 TypeScript 第一个版本(0.8),此前已经在微软内部开发了两年。

2014-10:Angular 发布了 2.0 版本,它是一个基于 TypeScript 开发的前端框架。

2015-04:微软发布了 Visual Studio Code,它内置了对 TypeScript 语言的支持,它自身也是用 TypeScript 开发的。

2016-05:@types/react 发布,TypeScript 可以开发 React 应用了。

2016-05:@types/node 发布,TypeScript 可以开发 Node.js 应用了。

2020-09:Vue 发布了 3.0 版本,官方支持 TypeScript。

TypeScript特点

TypeScript 增加了静态类型、类、模块、接口和类型注解(强调代码的模块化,面向对象)

TypeScript 更适合用于开发大型的应用(大型应用=模块的集成,大型应用优先需要易于维护,小应用优先需要开发效率)

TypeScript基本语法

基础数据类型

number(数字类型):双精度 64 位浮点值。它可以用来表示整数和分数。

string(字符串类型):一个字符系列,使用单引号(')或双引号(")来表示字符串类型。

boolean(布尔类型):表示逻辑值:true 和 false。

null:表示对象值缺失。

undefined:用于初始化变量为一个未定义的值。

never:never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值。

/* 字符串 */ 
const a: string = 'string'; 
/* 数字 */ 
const a: number = 1; 
/* 布尔值 */ 
const a: boolean = true;
/* null */ 
const a: null = null; 
/* undefined */ 
const a: undefined = undefined;

对象类型

const bytedancer: IBytedancer = {
    jobId:9303254,
    name: 'thr',
    sex: 'man',
    age: 20,
    hobby: 'TS',
}

interface IBytedancer {
    /*只读属性:不能修改对象初始化的值*/
    readonly jobId: number;
    name: string;
    sex: 'man' | 'woman' | 'other';
    age: number;
    /* 可选属性:定义该属性可以不存在*/
    boddy?: string;
    /*任意属性:所有对象属性都必须是该属性的子类型*/
    [key: string]: any;
}
/*报错:无法分配到jobId,它是只读属性*/
bytedancer.jobId= 12345;
/*成功:任意属性标注下可以添加任意属性*/
bytedancer.plateform= 'data';
/*报错:缺少属性'name',hobby可以缺省*/
const bytedancer2: IBytedancer = {
    jobId:56478,
    sex: 'woman',
    age: 18,
}

函数类型

/* 直接定义函数 */
function add(x: number, y: number): number {
    return x + y;
}

const mult: (x: number, y: number) => number = (x, y) => x * y;


interface IMult {
    (x: number, y: number): number;
}
const mult: IMult = (x, y) => x * y; 

interface IGetDate {
    (type: 'string', timestamp?: string): string;
    (type: 'date', timestamp?: string): Date;
    (type: 'string' | 'date', timestamp?: string): string | Date;
}


function add(x: number, y: number): number {
    return x + y;
}
const mult: (x: number, y: number) => number = (x, y) => x * y;

函数重载

// 函数的重载:函数的名称相同,但是参数不同的几个函数,就是函数的重载
// 定义重载函数
function add(num1: number, num2: number): number; // 没有函数体
function add(num1: string, num2: string): string; // 没有函数体
// 定义函数体,即实现函数,参数值类型一般比较宽泛
function add(num1: any, num2: any): any {
  if (typeof num1 === 'string' && typeof num2 === 'string') {
    return num1.length + num2.length
  }
  return num1 + num2
}
// 执行时根据参数的不同进行相应的重载函数,执行相同的函数体
const result = add(20, 30)
const result2 = add('abc', 'cga')
console.log(result);
console.log(result2);

// 在函数重载中,实现函数是不能被调用的
// add({name: 'tjx'}, {age: 18})

数组类型

// 类型+中括号 表示
type IArr1 = number[];
// 泛型表示
type IArr2 = Array<string | number | Record<string, number>>;
// 元祖表示
type IArr3 = [number, number, string, string];
// 接口表示
interface IArr4 {
    [key: number]: any
}

const arr1: IArr1 = [1, 2, 3, 4, 5, 6];
const arr2: IArr2 = [1, 2, '3', '4', { a: 1 }];
const arr3: IArr3 = [1, 2, '3', '4'];
const arr4: IArr4 = ['string', () => null, {}, []];

补充类型

// void:用于标识方法返回值的类型,表示该方法没有返回值。
type IEmptyFunction = () => void;
// any(任意类型):声明为 any 的变量可以赋予任意类型的值。
type IAnyType = any;
// enum(枚举类型):枚举类型用于定义数值集合。
enum EnumExample {
    add = '+',
    mult = '*',
}
EnumExample['add'] === '+';
EnumExample['+'] === 'add';

泛型 泛型就是对类型进行参数化。

function getRepeatArr(target) {
    return new Array(100).fill(target);
}
/*不预先指定具体的类型,而在使用的时候再指定类型的一种特性*/
type IGetRepeatArrR = <T>(target: T) => T[];

/*泛型接口&多泛型*/
interface IX<T, U> {
    key: T;
    val: U;
}
/*泛型类*/
class IMan<T> {
    instance: T;
}
/*泛型别名*/
type ITypeArr<T> =Array<T>;

类型别名&类型断言

// 使用type关键字定义类型别名
type IDType = string | number | boolean
type PointType = {
  x: number,
  y: number,
  z: number
}
function printID(id: IDType) {
}
function printPoint(point: PointType) {
}

// 类型断言就是将一个类型较大范围缩小为一个具体的类型,从而可以去调用该类型的相应的属性及方法
const el = document.getElementById('tjx') as HTMLImageElement
el.src = 'url地址'

class Person {
}
class Student extends Person {
  studying() {
  }
}
function sayHello(p: Person) {
  // p.studying() // 报错:类型“Person”上不存在属性“studying”。
  // 这时就可以使用类型断言
  (p as Student).studying()
}
const stu = new Student()
sayHello(stu)

字符串 数字 字面量

// 允许指定字符串 数字必须的固定值
type IDomTag = "html" | "body" | "div";
type IOddNumber = 1 | 2 | 3 | 4 | 5 | 6 ;

TypeScript高级类型

联合/交叉类型

联合类型是由两个或者多个其他类型组成的类型;
表示可以是这些类型中的任何一个值;
联合类型中的每一个类型被称之为联合成员(union’s members);
交叉类型(Intersection Types): 交叉类似表示需要满足多个类型的条件
交叉类型使用  &  符号

// number|string 联合类型
// 传入给一个联合类型的值是非常简单的:只要保证是联合类型中的某一个类型的值即可
// 但是我们拿到这个值之后,我们应该如何使用它呢?因为它可能是任何一种类型。
// 比如我们拿到的值可能是string或者number,我们就不能对其调用string上的一些方法;
function printID(id: number | string | boolean) {
  // console.log(id.toUpperCase()); // 会报错,id可能不是字符串类型,所以使用时要小心
  // 那么我们怎么处理这样的问题呢?
  // 我们需要使用缩小(narrow)联合;
  // TypeScript可以根据我们缩小的代码结构,推断出更加具体的类型;
  if (typeof id === 'string') {
    // typescript 可以帮助确定id一定时string类型
    console.log(id.toUpperCase);
  } else {
    console.log(id);
  }
}
printID(123)
printID("asd")
printID(true)

// 交叉类型
interface Colorful {
  color: string
}
interface IRun {
  running: () => void
}
type NewType = Colorful & IRun
const obj: NewType = {
  color: 'red',
  running: function() {
  }
}

类型保护

类型保护是一种TypeScript技术,用于获取变量类型信息,通常使用在条件块语句中。
下面的示例中,StudentId有一个string|number类型联合参数条目。我们看到,如果变量是string,则输出Student,如果是number,则输出Idtypeof类型保护符帮助我们从x参数中提取类型:

  function StudentId(x: string | number) {  
  if (typeof x == 'string') {  
  console.log('Student');  
  }  
  if (typeof x === 'number') {  
  console.log('Id');  
  }  
  }  
  StudentId(`446`); //prints Student  
  StudentId(446); //prints Id

高级类型

// 类型索引(keyOf)
// keyof 类似于 Object.keys ,用于获取一个接口中 Key 的联合类型
// demo
interface IPerson {
  age: number;
  gender: sex;
}
// 使用了keyOf后,只要IPerson修改了,type类型也会跟着自动修改
type personKeys = keyof IPerson;
//等价于
type personKeys = 'age' | 'gender';

// 类型映射(in)
// in用来做类型映射,遍历已有接口的key或者遍历联合类型
type Test<T> = {
   [P in keyof T]: T[P];  
};
// keyof T  相当于 type ObjKeys = 'a' | 'b'
// P in ObjKeys 相当于执行了一次 forEach 的逻辑,遍历 'a' | 'b'

interface IObj {
  a: string;
  b: string;
}

type newObj = Test<IObj>;

// 类型约束(extends)
// extends主要是用来对泛型加以约束的,他不像class使用extends是为了达到继承的目的。
// demo
 type BaseType = string | number | boolean;
 
 public testGenerics<T extends BaseType>(arg: T): T {
   return arg;
 }
 
 this.testGenerics('123'); // 成功
 this.testGenerics({}); // 失败
 
// extends的应用场景
// extends 经常与 keyof 一起使用,例如我们有一个方法专门用来获取对象的值,但是这个对象并不确定,我们就可以使用 extends 和 keyof 进行约束,具体例子如下:
// 根据传入的obj来约束key的值
function getValue<T, K extends keyof T>(obj: T, key: K) {
  return obj[key]
}

TypeScript工程应用

浏览器Web

  1. 配置webpack loader
  2. 配置tsconfig.js文件
  3. 运行webpack启动/打包
  4. loader处理ts文件时,会进行编译与类型检查

loader:
awesome-typescript-loader
babel-loader

node

使用TSC编译

image.png

  1. 安装Node和npm
  2. 使用npm安装tsc
  3. 配置tsconfig.js文件
  4. 使用tsc运行编译得到js文件

三、课后个人总结:

TypeScript语言内容还是非常丰富的,在本次课程中不可能讲完所有的语言基础知识以及写好该语言的全部方法。所以需要课下自己进行更多的学习,写代码终究还是实践性很强的一门课,需要自己多多实践,才能提升自身能力。

四、引用参考:

‌TypeScript入门.pptx