TypeScript笔记

TS的出现
TypeScript是JavaScript的超集,是具有静态类型的JavaScript。
TypeScript增强了代码的可维护性,因为正常写代码的时候鼠标悬浮上去就会给各种提示,告诉你当前这个变量接收什么类型的,如果类型不正确编译就不通过,直接提示错误信息;其次主流的框架Vue/React都全面支持了TypeScript(关键是国内大部分公司也开始陆陆续续使用了TypeScript开发项目),这门语言确实很好,但是需要学习成本...
运行条件
目前JavaScript可以在浏览器或node环境下直接运行,TypeScript是不能直接在浏览器或node环境下直接运行的,因为不认识。那怎么样才能认识TS呢?其实就是需要一些工具将类型擦除,那么最后生成的就是JS代码,就可以直接运行在以上两个环境了。
- esbuild
- swc
以上两种工具在类型擦除的时候不会进行类型检查,比如你定义了type A = numbbbber;这里的number是拼错了,但是以上两种工具不会报错提示类型numbbbber不存在,所以速度会比较快
- tsc
- babel
以上两种工具会进行类型检查。以上四种工具的使用方法可以查官网,最后就是生成一个新的可执行的js文件
也可以直接运行ts文件,可以在一些在线编辑器,比如playcode.io...也可以在本地使用ts-node,swc-node工具
数据类型
||数据类型| |-|-|-| |JS数据类型|null、undefined、string、number、boolean、bigint、symbol、Object(包括Array、Function、Date...)| |TS数据类型|以上所有JS的数据类型+void、nerver、enum、unknown、any+自定义类型type、interface|
JS的数据类型是具体的值的类型,TS的数据类型是集合
// js
let a = 1;
//ts
type number = 1 | 1.1 | ...
type Object = {?} | Array | Function | Sting | Number | Boolean | RegExp ...
// 类就是构造函数,比如Array,Function...
Object太大了一般都不会用到,以及String,Number,Boolean,这些构造函数只用于包装对象,正常开发中不用他们
// 包装对象
var n = 12;
console.log(n.toFixed(2)); //12.00
// 本身n是个number类型,n.toFixed的时候其实是对n进行new Number()的操作
如何在TS中描述对象的数据类型?
基本对象
- class/constructor
- type | interface
// class/constructor
const a: Array<number> = [1,2]
// type
type Person = {
name: string
age: number
}
// 索引签名
type A1 = {
[k: string]: number
}
//等价于Record泛型
type A2 = Record<string, number>
const p1: Person = {
name: 'jaclyn',
age: 25
}
const A3:A1 = {
xxx: 123
}
其他对象
- class(构造函数)
const d: Date = new Date();
const r: RegExp = new RegExp('a+b');
const r1: RegExp = /a+b/;
const m: Map<string,number> = new Map();
const wm: WeakMap<{name: string},number> = new WeakMap();
const s: Set<number> = new Set();
const ws: weakSet<{name:string}> = new weakSet();
如何描述数组对象?
由于Array太大不清晰所以TS开发者一般使用string[],Array<?>,[string, string]来描述数组
type A = string[] <==> type A = Array<string> // Array<string>就是泛型
type D = [string, string, string] // 三元组
如何描述函数对象?
由于Function太不精确,所以TS开发者一般用() => ?来描述函数
type fn = (a: number, b: number) => void
type fn2 = (a:string, b: string) => string
const uFn:fn = (x, y) => {
console.log('hello')
}
const uFn2:fn2 = (x, y) => {
return [x,y].join(' ');
}
uFn(1,2)
console.log(uFn2("hello", "jaclyn"))
// 箭头函数没有this,如何使用this
type Person = {
name: string
age: number
}
type fnWithThis = (this: Person, name: string) => void
const fnThis: fnWithThis = function() {
console.log(this.name)
}
// 这里使用的是普通函数,如果使用箭头函数,这里的this指向的就是GlobalThis
const x:Person = {
name: 'jaclyn',
age: 25
}
fnThis.call(x, 'xxx'); // jaclyn
// 带有属性的函数声明
type fnWithProps = {
(a: number, b: number) => number
props: string
}
const a: fnWithProps = (x, y) => {
return x + y;
}
a.props = "hello"
console.log(a.props)
any和unknow的区别
any代表任何类型,ts绝大部分规则对any不生效;unknow代表不知道是什么类型,比如从外部获取到的数据可以通过一个unknow类型的变量进行接收并对变量进行as断言,这样就可以使用对应的方法
const uk: unknow = ajax.get('xxx');
const b = uk as string; // 类型断言
b.split('a'); // 因为进行了断言所以就可以使用string下的split方法
nerver类型怎么用?
一般nerver类型就是不能用了,因为他就是个空集,一般可以作为类型判断
type A = string | number | boolean;
const B: A = ajax.get('xxx');
if(typeof B === 'string') {
// 此时B就是个string类型
}else if(typeof B === number) {
// 此时B就是个number类型
}else if(typeof B === boolean) {
// 此时B就是个boolean类型
}else {
// 此时B就是个nerver类型不能被使用
}
enum的使用
一般在需要被映射的时候使用,如果需要遍历就正常看做是个对象使用for in进行key的遍历就行了
enum A {
todo: 0,
done, //不定义默认+1
}
let status: A = 0;
let status1: A = A.todo;
type和interface什么时候用?
- type类型别名,只是给其他类型取个名字,type可以描述所有类型包括基本类型string,number,boolean等以及对象类型;interface是类型声明,interface只能描述对象类型,包括普通对象以及Array,Function,Date...interface就是为了迎合面向对象(oop)编程而出的
- type不可扩展,不可重复赋值;interface可扩展,重复赋值的话会自动合并。所以一般对外api尽量使用interface,方便别人扩展,对内api尽量用type,防止代码分散
提示类型系统有哪些运算?
联合类型(A | B)
联合类型就是并的意思,可以是A也可以是B,所以不确定类型到底属于哪一种,所以需要类型收窄,使用的方法包括:
- js方法:typeof对简单类型的判断,instanceof对象类型的判断
- ts方法:类型谓词/类型判断 is
- ts方法:x.kind(添加一个同名的可辨别的简答类型的key)
- ts方法:断言,断言了就类似强制性的属于某种类型
// typeof
const f1 = (a: number | string) => {
if(typeof a === 'number') {
//此时a就是number类型
}else if(typeof a === 'string') {
// 此时a就是string类型
}else {
// 此时a就是nerver类型
}
}
// instanceof
const f2 = (a: Date | Date[]) => {
if(a instanceof Date) {
// 此时a就是Date类型
}else if(a instanceof Array) {
// 此时a就是Array类型
}else {
// 此时a就是nerver类型
}
}
// 以上type和instanceof可以判断基本类型以及复杂类型,但是不能判断ts类型
// 通过key in的方式判断
type Person = {
name: string
age: number
}
type Animal = {
x: string
}
const f3 = (a: Person | Animal) => {
if('name' in a) {
// Person类型
}else if('x' in a) {
// Animal类型
}else // 此时a就是nerver类型
}
// 通过类型谓词is,可以支持所有ts类型,但是很麻烦
type Rect = {
width: number
height: number
}
type Circle = {
center: number
radius: number
}
const fn = (a: Rect | Circle) => {
if(isRect(a)) {
// 此时a就是Rect类型
}else if(isCircle(a)) {
// 此时a就是Circle类型
}
}
function isRect(x: Rect | Circle) : x is Rect {
return 'width' in x && 'height' in x;
// 这里的逻辑判断可以随便写,最后返回true|false就行,关键是x is Rect
}
function isCircle(x: Rect | Circle) : x is Circle {
return 'center' in x && 'radius' in x;
}
// 通过a.kind,其实就是添加一个同名的可辨别的简单类型的key
type Circle = { kind: 'Circle', x?: [number, number] };
type Square = { kind: 'Square', x: number }; // 这里都有x所以不能通过key in的方式判断
type Shape = Circle | Square;
const fn2 = (x: Shape) => {
if(x.kind === 'Circle') {
// 此时a就是Circle类型
}else if(x.kind === 'Square') {
// 此时a就是Square类型
}else // 此时a就是nerver类型
}
交叉类型
type使用&模拟继承;interface使用extends实现继承
type A = {
name: string
}
type B = {
age: number
} & A
interface C {
name: string
}
interface D extends C {
age: number
}
const a: B = {
age: 13,
name: 'hh'
}
const b: D = {
age: 13,
name: 'hh'
}