TypeScript学习
- ts基础类型:string、number、boolean、any、unknown(只能作为父类,赋值只能给any、unknown两种类型)、null、undefined、void
- 接口interface限制类型,使用了任意类型[propName: string]: any;,那么确定属性和可选属性的类型都必须是它的类型的子集。即整个接口里若任意类型为string,那么整个接口的类型都为string
- 类型断言写法:(num).length (num as string).length
any可以断言为任何类型,用any来做临时断言 - 交叉类型使用&符号将多个接口连接
- 内置对象:Boolean、Number、string、RegExp、Date、Error
- DOM和BOM上的对象:Document、HTMLElement、Event、NodeList
//读取div 这种需要类型断言 或者加个判断应为读不到返回null
let div:HTMLElement = document.querySelector('div') as HTMLDivElement
- Promise 如果我们不指定返回的类型TS是推断不出来返回的是什么类型
- ts类型定义在源码的src/lib文件夹下
- 类中的静态成员只能供静态方法访问使用,静态方法中访问不到public、protected、private修饰的变量。静态方法可以调用其他静态方法。
- never类型:表示不应该存在的状态
应用场景:
// 只会抛出异常没有返回值
function Never():never {
throw new Error('aaa')
}
// 因为存在死循环,所以 loop 将不会有返回值
function loop(): never {
while (true) {
}
}
// 在兜底逻辑里定义一个never变量,接口c如果不在函数中定义逻辑那一定会走default出口,因为never类型只能作为父类型,不能让子类型给他赋值,所有在编译会报错。
interface A {
type: "foo"
}
interface B {
type: "bar"
}
interface C {
type: "bizz"
}
type All = A | B | C;
function handleValue(val: All) {
switch (val.type) {
case 'foo':
break;
case 'bar':
break
default:
//兜底逻辑 一般是不会进入这儿如果进来了就是程序异常了
const exhaustiveCheck: never = val;
break
}
}
- symbol类型:可以传递参做为唯一标识 只支持 string 和 number类型的参数。常用于作为对象的key值。
使用symbol定义对象的属性,不能通过一下四种方式拿到:
- for in 遍历
- Object.keys(对象名) 遍历
- Object.getOwnPropertyNames(对象名)
- JSON.stringify(对象名)
获取symbol定义的属性的方法:
- Object.getOwnPropertySymbols(对象名)//拿到具体的symbol 属性,对象中有几个就会拿到几个(只拿symbol类型的)
- Reflect.ownKeys(对象名)// 拿到对象的所有属性
- Symbol.iterator 迭代器:支持遍历大部分类型迭代器 arr、nodeList、argumetns、set 、map等。
let arr: Array<number> = [4, 4, 5]
let it: Iterator<number> = arr[Symbol.iterator]()
console.log(it.next()) // 输出第一个元素4
console.log(it.next())// 输出第二个元素4
console.log(it.next())// 输出第三个元素5
console.log(it.next()) //输出undefined
输出结果:
{ value: 4, done: false }
{ value: 4, done: false }
{ value: 5, done: false }
{ value: undefined, done: true }
value表示值,done表示是否不能迭代了,true表示后面没有值了,已经结束了
- 生成器for of 在js底层已经帮我们完成了迭代功能,使用的是iterator,所以for of不支持遍历对象。因为对象上没有Symbol.iterator 迭代器。
for of底层代码基础版封装:
const gen = (erg:any): void => {
let it: Iterator<any> = erg[Symbol.iterator]()
let next:any= { done: false }
while (!next.done) {
next = it.next()
if (!next.done) {
console.log(next.value)
}
}
}
- for in和for of的区别:
(1)for in既能遍历对象也能遍历数组;for of不能遍历对象
(2)for in遍历数组时的key是数组的索引;for of是数组的那个值
-
泛型:语法为函数名字后面跟一个<参数名> 参数名可以随便写 例如我这儿写了T。当我们使用这个函数的时候把参数的类型传进去就可以了 (也就是动态类型)
-
泛型约束:对使用的泛型进行约束,我们约束其为具有length属性的类型。
interface Len {
length:number
}
function getLegnth<T extends Len>(arg:T) {
return arg.length
}
getLegnth<string>('123')
- 使用keyof 约束对象:首先定义了T类型并使用extends关键字继承object类型的子类型,然后使用keyof操作符获取T类型的所有键,它的返回类型是联合类型,最后利用extends关键字约束 K类型必须为keyof T联合类型的子类型。
// 在这个例子中:T:let o : { a: number, b: number, c: number}
// K:就是T中对象的所有key值组成的联合类型,即a|b|c
function prop<T, K extends keyof T>(obj: T, key: K) {
return obj[key]
}
let o = { a: 1, b: 2, c: 3 }
prop(o, 'a')
-
泛型类:声明方法跟函数类似名称后面定义<类型>。
定义时: 类名 // 这里T可以为任意名字,就是一个泛型的意思
实例化时:new 类名<具体类型> -
三斜线指令:/// 指令是三斜线指令中最常见的一种。
它用于声明文件间的依赖。它可以告诉编译器在编译过程中要引入的额外的文件。
引入声明文件:/// 引入到声明文件,表明这个文件使用了 @types/node/index.d.ts里面声明的名字; 并且,这个包需要在编译阶段与声明文件一起被包含进来。 仅当在你需要写一个d.ts文件时才使用这个指令。
如果是下载的引入声明文件回去node_modules里去找@types/node/index.d.ts -
声明文件d.ts:当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。
declare var 声明全局变量
declare function 声明全局方法
declare class 声明全局类
declare enum 声明全局枚举类型
declare namespace 声明(含有子属性的)全局对象
interface 和 type 声明全局类型
/// <reference /> 三斜线指令
在引入第三方模块时,如果第三方包没有声明文件会出现红色下划线。
查看第三方包的声明文件:找到node_modules下第三方包的文件夹,找到package.json文件在types配置中会定义这个包的声明文件。
如果第三方包没有声明文件,需要我们自己定义。
定义方式:(1)在项目根目录下创建一个文件,文件名为第三方包名.d.ts
(2)在文件中写入一下代码
以express为例:declare const express: ()=> any;
第三方声明文件的包查看在这个网址https://www.npmjs.com/~types?activeTab=packages
- mixins混入:可以把他看作为合并
(1)对象混入:可以使用es6的Object.assign(对象1,对象2,…) 合并多个对象
(2)类混入:使用implements, 把类当成了接口。
混入类的理解:就是很多个已经写好的类,现在需要定义一个新类,需要之前定义好的类上的方法,实现整合(合并)。这个新类通过implements关键字实现之前定义好的类,在新类中定义好所有属性和方法的占位符。通过自己定义一个mixin函数实现将其他类的属性方法都放在新类中。
// A、B已经定义好的类
class A {
type: boolean = false;
changeType() {
this.type = !this.type
}
}
class B {
name: string = '张三';
getName(): string {
return this.name;
}
}
// C类需要A、B类上的所有方法和属性
// C类在这里提前写好占位符,通过自定义一个函数实现各属性和方法
class C implements A,B{
type:boolean
changeType:()=>void;
name: string;
getName:()=> string
}
Mixins(C, [A, B])
// Object.getOwnPropertyNames()可以获取对象自身的属性,除去他继承来的属性,对它所有的属性遍历,它是一个数组,遍历一下它所有的属性名
function Mixins(curCls: any, itemCls: any[]) {
itemCls.forEach(item => {
Object.getOwnPropertyNames(item.prototype).forEach(name => {
curCls.prototype[name] = item.prototype[name]
})
})
}
-
装饰器:一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上。通过@函数名使用
若要启用实验性的装饰器特性,你必须在命令行或tsconfig.json里启用编译器选项"experimentalDecorators": true,
装饰器类型:ClassDecorator 、MethodDecorator、PropertyDecorator、ParameterDecorator -
rollup构建ts项目:
(1)创建rollup.config.js文件
(2)下载依赖:1.全局安装rollup npm install rollup-g
2.安装TypeScript npm install typescript -D
3.安装TypeScript 转换器 npm install rollup-plugin-typescript2 -D
4安装代码压缩插件 npm install rollup-plugin-terser -D
5 安装rollupweb服务 npm install rollup-plugin-serve -D
6 安装热更新 npm install rollup-plugin-livereload -D
7引入外部依赖 npm install rollup-plugin-node-resolve -D
8安装配置环境变量用来区分本地和生产 npm install cross-env -D
9替换环境变量给浏览器使用 npm install rollup-plugin-replace -D
(3)配置rollup.config.js文件 -
webpack构建ts项目:
(1)创建配置文件webpack.config.js
(2)安装依赖:安装webpack npm install webpack -D
webpack4以上需要 npm install webpack-cli -D
编译TS npm install ts-loader -D
TS环境 npm install typescript -D
热更新服务 npm install webpack-dev-server -D
HTML模板 npm install html-webpack-plugin -D -
proxy和reflect实现数据:
proxy函数有两个参数(target目标对象,handler{get函数,set函数})
get函数读取操作的捕捉器。有三个参数(target目标对象,prop对象上的某个属性,receiver和target的值一样)
set函数设置操作的捕捉器。有四个参数(target目标对象,prop对象上的某个属性,value修改的值,receiver和target的值一样(目的为了防止上下文错误))
Reflect的所有属性和方法都是静态的。
Reflect.get(target, name, receiver) //查找并返回target对象的name属性,如果没有该属性返回undefined
Reflect.set(target, name,value, receiver) //设置target对象的name属性等于value -
Partial & Pick:TS内置的高级类型
(1)Partial将泛型变量T中的所有属性设置为可选
// 源码实现:
type Partial<T> = {
[P in keyof T]?: T[P];
}
// 理解:keyof将T中所有属性变为联合类型,in遍历这个联合类型中的每一项,即遍历了这个对象上所有的属性,将其变为可选的。T[P] 索引访问操作符,与 JavaScript 种访问属性值的操作类似,下面案例中的T[P]为string或者number。
例子:
type Person = {
name:string,
age:number
}
type p = Partial<Person>
理解:将Person这个类型中的属性都变为可选的,使用了Partial之后变为p变为:
type p = {
name ?: string,
age?: number
}
(2)Pick:从类型定义T的属性中,选取指定一组属性,返回一个新的类型定义。
源码实现:
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
}
// 理解:讲一个类型中部分属性取出来组成一个新的类型,就是过滤出来我们需要的属性
例子:
type Person = {
name:string,
age:number,
text:string
address:string
}
type Ex = "text" | "age"
type A = Pick<Person,Ex>
// 理解:就是把原先Person中的text和age属性过滤出来组件新类型A
- Record & Readonly:
(1)Readonly:将该属性变为只读,将每一个属性变成只读
源码实现:
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
(2)Record:做到了约束 对象的key 同时约束了 value
源码实现:
type Record<K extends keyof any, T> = {
[P in K]: T;
}
// 理解:keyof any 返回 string number symbol 的联合类型;说明key只能传 string number symbol 这三种类型的变量,value就是你传来什么类型,所有属性都要符合这个类型。对key和value都做了约束。
- infer:ts新增到的关键字 充当占位符
(1)infer一般用于类型约束extends后面
(2)限制元祖的数据类型:
type TupleToUni<T> = T extends Array<infer E> ? E : never
type TTuple = [string, number];
type ToUnion = TupleToUni<TTuple>; // string | number
提取头部元素
type Arr = ['a','b','c'] //这个值是用来限制元素是什么类型的
type First<T extends any[]> = T extends [infer First,...any[]] ? First : []
type a = First<Arr>
let num: a = 'a'
// 理解:本案例中提取a类型,且a类型的值只能为a