01学习TypeScript

01学习TypeScript

  • 1. TypeScript出现的原因
    • 1.1 JavaScript的痛点
    • 1.2 TypeScript类型校验
  • 2. 认识TypeScript
    • 2.1定义
    • 2.2 TypeScript应用的项目
    • 2.3 TypeScript的编译环境
      • 安装命令
      • 查看版本
    • 2.4 TypeScript的运行环境
  • 3. TypeScript基础语法
    • 3.1 声明变量(标识符)
    • 3.2 变量的类型推导
    • 3.3 数据类型在TS中的使用
    • 3.4 数据类型在函数中的演练
      • 指定参数类型
      • 指定返回值类型
      • 函数类型演练-使用第三方库
      • 匿名函数的参数
      • 函数里对象类型和可选类型的使用
    • 3.5 TypeScript类型
      • any类型
      • unknown类型
      • void类型
      • never类型
      • tuple元组类型
  • 写在最后

1. TypeScript出现的原因

任何新技术的出现都是为了解决原有技术的某个痛点。

1.1 JavaScript的痛点

  • ES5以及之前的使用的var关键字关于作用域的问题;
  • 最初JavaScript设计的数组类型并不是连续的内存空间;
  • 直到今天JavaScript也没有加入类型检测机制;
    类型带来的问题是JavaScript没有对我们传入的参数进行任何的限制,只能等到运行期间才发现这个错误;并且当这个错误产生时,会影响后续代码的继续执行,也就是整个项目都因为一个小小的错误而陷入崩溃;
    比如:
function getLength(args) {console.log(args.length);
}getLength("1234")   //4
getLength([1, 2, 4]) //3getLength(12) //undefined  数字在进行运算时自动创建包装类型Number(new Number())对象但是{}没有length属性  getLength({})
getLength() //undefined.length    Uncaught TypeError: Cannot read properties of undefined (reading 'length')
console.log("后续代码不执行");

上面代码中自定义getLength函数,打印传入参数的长度

  • 传入的参数为字符串和数组因为有length属性,可以正常运行;
  • 但是有可能别人没有传入参数就会报错,

错误应该在编写代码时候被发现而不是运行到浏览器才发现
而且如果我们是调用别人的类库,又如何知道让我们传入的到底是什么样的参数呢?
但是,如果给JavaScript加上很多限制,在开发中就可以很好的避免这样的问题了

  • 比如getLength函数中str是一个必传的类型,没有调用者没有传编译期间就会报错;
  • 比如我们要求它的必须是一个String类型,传入其他类型就直接报错;
  • 那么就可以知道很多的错误问题在编译期间就被发现,而不是等到运行时再去发现和修改;

1.2 TypeScript类型校验

TypeScript提供了类型校验,在开发阶段提前帮助发现错误

// getLength(args:string | any[]): 限制传入的参数是string类型,或者Array类型(数组里值可以是任意类型)
// getLength(args:{length:number}):限制传入的参数是对象,具有length属性且值是number类型
function getLength(args:{length:number}){console.log(args.length);
}getLength("aaaa")//字符串在进行运算时自动创建包装类型字符串对象,有length属性 值为4
getLength(["a","b",123]) //数组也是对象,有length属性 值为3const info = {length:200
}
getLength(info)//info对象有length属属性值200是number类型export {}

以上为正确的调用
错误的调用getLength(123)getLength()TypeScript 静态类型检查器会利用类型系统提供的信息,并在事态发展不对劲的时候告知我们,执行代码之前首先抛出一个错误

2. 认识TypeScript

2.1定义

TypeScript是拥有类型的JavaScript超集,它可以编译成普通、干净、完整的JavaScript代码。

  • 加强版的JavaScript,JavaScript所拥有的特性,TypeScript全部都支持,并且紧随ECMAScript的标准,ES6、ES7、ES8等新语法标准,都支持;
  • TypeScript在实现新特性的同时,总是保持和ES标准的同步甚至是领先;
  • 并且在语言层面上,不仅仅增加了类型约束,而且包括一些语法的扩展,比如枚举类型(Enum)、元组类型(Tuple)等;
  • 并且TypeScript最终会被编译成JavaScript代码,所以你并不需要担心它的兼容性问题,在编译时也可以不借助于Babel(ES13转成ES5)这样的工具,vue项目一般借助于Babel(TS转成js(es5))而不是TSC;

可以把TypeScript理解成更加强大的JavaScript,不仅让JavaScript更加安全,而且给它带来了诸多好用特性;

2.2 TypeScript应用的项目

  1. Angular源码在很早就使用TypeScript来进行了重写,并且开发Angular也需要掌握TypeScript;
  2. Vue3源码也采用了TypeScript进行重写,在阅读源码时会看到大量TypeScript的语法;
  3. 目前最流行的编辑器VSCode也是使用TypeScript来完成的;
  4. 包括React中已经使用的ant-design的Ul库,也大量使用TypeScript来编写;
  5. 目前公司非常流行Vue3+TypeScriptReact+TypeScript的开发模式;
  6. 小程序开发,也支持TypeScript;

2.3 TypeScript的编译环境

TypeScript最终会被编译成JavaScript来运行,所以需要搭建对应的环境,就可以通过TypeScript的Compiler将其编译成JavaScript运行到浏览器上
全局安装:

安装命令

npm install typescript -g

查看版本

tsc --version

2.4 TypeScript的运行环境

// string: TypeScript给我们定义标识符时,提供的字符串类型
// String: Javascript中字符串的包装类
let message: string = "hello World"
//message = 123//不能将类型“number”分配给类型“string”
message = "123"
function logMessage(msg:string){console.log(msg);
}logMessage(message)	//123

编写完ts代码需要经过tsc 文件名.ts命令编译成.js代码,再把对应的.js代码在浏览器或者Node环境下运行
简化运行步骤的两种方案

  1. 编写了TypeScript之后,直接运行在浏览器上
    • 通过webpack,配置本地的TypeScript编译环境和开启本地服务,直接运行在浏览器上
    • 使用Webpack搭建TypeScript编译环境
  2. 编写了TypeScript之后,直接通过node的命令来执行
    • 通过ts-node库,为TypeScript的运行提供执行环境;
    • 安装ts-node: npm install ts-node -g
    • 另外ts-node需要依赖tslib和@types/node两个包: npm install tslib @types/node -g
    • 现在,可以直接通过ts-node来运行TypeScript的代码: ts-node 文件名.ts

3. TypeScript基础语法

3.1 声明变量(标识符)

声明数据类型后TypeScript就会进行类型检测,数据类型可以称之为类型注解(Type Annotation)
完整写法格式:
var/let/const 标识符:数据类型 = 赋值;==var/let/const 标识符:类型注解 = 赋值;

// 定义标识符.不推荐var因为没有块级作用域
let name: string = "coder"
const age: number = 18
const height: number = 1.88

3.2 变量的类型推导

开发中,为了方便并不会在声明每一个变量时都写上对应的数据类型,TypeScript本身的
特性可以帮助我们推断出对应的变量类型:

// 声明一个标识符时,如果有直接进行赋值,会根据赋值的类型推导出标识符的类型注解
// 这个过程称之为类型推导
// let进行类型推导, 推导出来的通用类型
// const进行类型推导,推导出来的字面量类型(后续专门讲解)
let name = "why"
let age = 18
const height = 1.88
name = 123   //上面虽然没有直接声明变量(标识符)的数据类型但是会自动推导出标识符的类型注解//不能将类型“number”分配给类型“string”。export {}

3.3 数据类型在TS中的使用

JS中的number,boolean,string数据类型在TS中的使用

//number   类型推导写法和明确指定变量类型
let num1 = 123
let num2: number= 456//boolean
let flag1 = true
let flag2: boolean= true//string  
let str1 = "abc"
let str2:string = "abc"//null
let null1 = null
let null2: null = null//undefined
let unde1 = undefined
let unde2:undefined = undefinedexport {}

JS中的Array数据类型在TS中的使用

// 明确的指定<数组>的类型注解: 两种写法
// 1. string[]:数组类型,并且数组中存放的字符串类型
// 2. Array:数组类型,并且数组中存放的是字符串类型// 注意事项: 在真实的开发中, 数组一般存放相同的类型,不要存放不同的类型
// 写法一:常用
let names: string[] = ["花花","红红","灰灰"]
names.push("菲菲")
//names.push(123)//错误的写法// 写法二:泛型特殊情况才用到
let nums:Array<number> = [1,2,3]export {}

JS中的Object数据类型在TS中的使用

// 类型推导 实际上  let info = {name:string;age:number;}
let info = {name:"huihui",age:18
}
console.log(info.name);export {}

JS中的Symbol数据类型在TS中的使用

const s1:symbol = Symbol("festival")
const s2:symbol = Symbol("festival")const person = {[s1]:"父亲节",[s2]:"母亲节"
}

3.4 数据类型在函数中的演练

指定参数类型

//在定义一个TypeScript中的函数时,都要明确的指定参数的类型
function sum(num1:number,num2:number){return num1 + num2
}// 类型推导出res变量返回值类型为number,可以不指定返回值类型
const res = sum(123,123)// sum({},{})  类型“{}”的参数不能赋给类型“number”的参数
export {}

指定返回值类型

//明确指定返回值类型
//返回值类型可以明确的指定,也可以自动进行类型推导
function sum(num1:number,num2:number): number{return num1 + num2
}const res = sum(123,123)// sum({},{})类型“{}”的参数不能赋给类型“number”的参数
export {}

函数类型演练-使用第三方库

type LyricType={time:number,text:string
}// 歌词解析工具:一般第三方库源码写上返回值类型一眼就知道下面的返回值    是数组类型并且数组里存放的是对象对象里包含time和text属性
function parseLyric(lyric:string):LyricType[]{const lyrics: LyricType[] = []lyrics.push({time:1111,text:"天青色等烟雨"})return lyrics
}const lyricInfos = parseLyric("fasdsds")
for (const item of lyricInfos) {console.log(item.time,item.text);
}

匿名函数的参数

const names: string[] = ["a","b","c"]// 匿名函数是否需要添加类型注解呢? 最好不要添加类型注解
// 因为TypeScript会根据forEach函数的类型以及数组的类型推断出item的类型;
// 这个过程称之为上下文类型(contextual typing),因为函数执行的上下文可以帮助确定参数和返回值的类型;
names.forEach(function(item,index,arr){console.log(item,index,arr);
})export {}

函数里对象类型和可选类型的使用

// 1.对象类型的简单回顾
const info: {name: string,age: number
} = {name: "why",age: 18
}//2.对象类型和函数类型结合使用   z可选类型(可传可不传)
type PointType = { x: number, y: number, z?: number }function printCoordinate(point: PointType) {console.log("x坐标", point.x);console.log("y坐标", point.y);
}// printCoordinate(123) 错误调用
printCoordinate({ x: 123, y: 1 })export { }

3.5 TypeScript类型

any类型

在某些情况下,确实无法确定一个变量的类型并且可能它会发生变化,这个时候可以用any类型
any类型有点像一种讨巧的TypeScript手段:

  • 我们可以对any类型的变量进行任何的操作,包括获取不存在的属性、方法;
  • 我们给一个any类型的变量赋值任何的值,比如数字、字符串的值;
// any类型就表示不限制标识符的任意类型,并且可以在该标识符上面进行任意的操作(在TypeScript中回到Javascript中)
let id: any = 123id = "bbbb"
id = true
console.log(id.length);//隐患不安全
id = { name: "lihua", age: 18 }// 定义数组
const infos:any[] = ["liming",123,true,{},[]]export {}

如果对于某些情况的处理过于繁琐不希望添加规定的类型注解,或者在引入一些第三方库时,缺失了类型注解,这个时候我们可以使用any:包括在Vue源码中,也会使用到any来进行某些类型的适配;

unknown类型

和any类型有点类似,unknown类型的值上做任何事情都是不合法的,必须进行类型的校验(缩小), 才能根据缩小之后的类型, 进行对应的操作,保证代码的安全;

let foo: unknown = "aaa"
foo = 123// unknown类型默认情况下在上面进行任意的操作都是非法的
// 要求必须进行类型的校验(缩小), 才能根据缩小之后的类型, 进行对应的操作
if(typeof foo === "string"){    	//类型缩小console.log(foo.length, foo.split(" "));
}export {}

void类型

  1. 在TS中如果一个函数没有任何的返回值,那么返回值的类型就是void类型
  2. 如果返回值是void类型,那么我们也可以返回undefined(TS编译器允许这样做而己)
function sum(num1: number, num2: number): void {console.log("没有返回值");
}// 应用场景: 用来指定函数类型的返回值是void
type FooType = () => void
const foo: FooType = () => { }// 举个例子:定义要传递的参数是函数类型才指定void(涉及给函数定义类型问题,后续还会详细讲解))
// 1.定义要求传入的函数的类型,参数和类型任意
type ExecFnType = (...args: any[]) => void
// type ExecFnType = (name:string,age:number) => void// 2.定义一个函数,并且接收的参数也是一个函数,而且这个函数的类型必须是ExecFnType
function delayExecFn(fn:ExecFnType){setTimeout(()=>{fn("huahua",18)},1000)
}
// 3.执行上面的函数,并且传入一个匿名函数
delayExecFn((name,age)=>{console.log(name,age);
})export { }

never类型

never的应用场景:

  1. 开发中很少实际去定义never类型
  • 某些情况下会自动进行类型推导出never
  1. 开发框架(工具)的时候可能会用到never
  2. 封装一些类型工具的时候,可以使用never
  • 类型体操的题目: never
// 二,封装框架/工具库的时候可以使用一下never
// 其他人在扩展工具的时候, 对于一些没有处理的case, 会直接报错,提醒需要加对应case的处理
function handleMessage(message:string | number){switch(typeof message){case "string":console.log(message.length);breakcase "number":console.log(message);breakdefault:// never永远不会发生值的类型const check:never = message}
}handleMessage("abc")
handleMessage(123)// 另外的同事调用这个函数
handleMessage(true) 

上面代码中 handleMessage(true) 后加| boolean,check会报错,因为没加对应的case处理,会报错提醒

tuple元组类型

tuple和数组区别:

  • 首先,数组中通常建议存放相同类型的元素,不同类型的元素是不推荐放在数组中。(可以放在对象或者元组中)
  • 其次,元组中每个元素都有自己特性的类型,根据索引值获取到的值可以确定对应的类型;
// 使用元组类型
// 元组数据结构中可以存放不同的数据类型,·取出来的item也是有明确的类型
const info3: [string, number, number] = ["lemon", 22, 1.88]
const value2 = info3[2]// 在函数中返回多个类型的值时使用元组类型,最方便
function useState(initialState: number): [number, (newValue: number) => void] {let stateValue = initialStatefunction setValue(newValue: number) {stateValue = newValue}return [stateValue, setValue]
}const [count, setCount] = useState(10)
console.log(count);//10
setCount(100)
console.log(count);//100export { }

写在最后

本篇文章到这里就结束了,如果文章对你有用,可以三连支持一下,如果文章中有错误或者说你有更好的见解,欢迎指正~
代码地址: lemonDin/learn_typescript(github.com).

PS:代码和思路来自王红元(coderwhy)老师,在其中做个总结


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部