[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qOzCqWh9-1646827195897)(./images/TS_logo.png)]
TypeScript
简称:TS,是 JavaScript 的超集,简单来说就是:JS 有的 TS 都有[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R4Cwn7L2-1646827195898)(./images/TS是JS的超集.png)]
Type
+ JavaScript(在 JS 基础之上,为 JS 添加了类型支持)[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y1qrLGFN-1646827195899)(./images/TSvsJS.png)]
为什么会这样?
从编程语言的动静来区分,TypeScript 属于静态类型的编程语言,JavaScript 属于动态类型的编程语言
代码编译和代码执行的顺序:1 编译 2 执行
对于 JS 来说:需要等到代码真正去执行的时候才能发现错误(晚)
对于 TS 来说:在代码编译的时候(代码执行前)就可以发现错误(早)
并且,配合 VSCode 等开发工具,TS 可以提前到在编写代码的同时就发现代码中的错误,减少找 Bug、改 Bug 时间
对比:
除此之外,Vue 3 源码使用 TS 重写、Angular 默认支持 TS、React 与 TS 完美配合,TypeScript 已成为大中型前端 项目的首选编程语言
目前,前端最新的开发技术栈:
npm i -g typescript
tsc
命令,实现了 TS -> JS 的转化sudo
获取权限:sudo npm i -g typescript
yarn 全局安装:sudo yarn global add typescript
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zp6SO1qm-1646827195900)(./images/TS编译.png)]
.ts
)tsc hello.ts
(此时,在同级目录中会出现一个同名的 JS 文件)node hello.js
1 创建 ts 文件 ===> 2 编译 TS ===> 3 执行 JS
ts-node
包,直接在 Node.js 中执行 TS 代码npm i -g ts-node
ts-node
命令ts-node hello.ts
相当于:1 tsc 命令 2 node(注意:ts-node 不会生成 js 文件)解释:ts-node 命令在内部偷偷的将 TS -> JS,然后,再运行 JS 代码
示例代码:
let age: number = 18
: number
就是类型注解// 错误代码:
// 错误原因:将 string 类型的值赋值给了 number 类型的变量,类型不一致
let age: number = '18'
可以将 TS 中的常用基础类型细分为两类:1 JS 已有类型 2 TS 新增类型
number/string/boolean/null/undefined/symbol
object
(包括,数组、对象、函数等对象)let age: number = 18
let myName: string = '老师'
let isLoading: boolean = false
// 等等...
number[]
写法// 写法一:
let numbers: number[] = [1, 3, 5]
// 写法二:
let strings: Array<string> = ['a', 'b', 'c']
需求:数组中既有 number 类型,又有 string 类型,这个数组的类型应该如何写?
let arr: (number | string)[] = [1, 'a', 3, 'b']
|
(竖线)在 TS 中叫做联合类型,即:由两个或多个其他类型组成的类型,表示可以是这些类型中的任意一种类型别名(自定义类型)
:为任意类型起别名type CustomArray = (number | string)[]
let arr1: CustomArray = [1, 'a', 3, 'b']
let arr2: CustomArray = ['x', 'y', 6, 7]
type
关键字来创建自定义类型函数参数
和返回值
的类型// 函数声明
function add(num1: number, num2: number): number {
return num1 + num2
}
// 箭头函数
const add = (num1: number, num2: number): number => {
return num1 + num2
}
type AddFn = (num1: number, num2: number) => number
const add: AddFn = (num1, num2) => {
return num1 + num2
}
void
function greet(name: string): void {
console.log('Hello', name)
}
void
类型// 如果什么都不写,此时,add 函数的返回值类型为: void
const add = () => {}
// 这种写法是明确指定函数返回值类型为 void,与上面不指定返回值类型相同
const add = (): void => {}
// 但,如果指定 返回值类型为 undefined,此时,函数体中必须显示的 return undefined 才可以
const add = (): undefined => {
// 此处,返回的 undefined 是 JS 中的一个值
return undefined
}
slice()
也可以 slice(1)
还可以 slice(1, 3)
function mySlice(start?: number, end?: number): void {
console.log('起始索引:', start, '结束索引:', end)
}
?
(问号)// 空对象
let person: {} = {}
// 有属性的对象
let person: { name: string } = {
name: '同学'
}
// 既有属性又有方法的对象
// 在一行代码中指定对象的多个属性类型时,使用 `;`(分号)来分隔
let person: { name: string; sayHi(): void } = {
name: 'jack',
sayHi() {}
}
// 对象中如果有多个类型,可以换行写:
// 通过换行来分隔多个属性类型,可以去掉 `;`
let person: {
name: string
sayHi(): void
} = {
name: 'jack',
sayHi() {}
}
{}
来描述对象结构属性名: 类型
的形式方法名(): 返回值类型
的形式{}
形式为对象添加类型,会降低代码的可读性(不好辨识类型和值)// 创建类型别名
type Person = {
name: string
sayHi(): void
}
// 使用类型别名作为对象的类型:
let person: Person = {
name: 'jack',
sayHi() {}
}
type Person = {
greet(name: string): void
}
let person: Person = {
greet(name) {
console.log(name)
}
}
type Person = {
greet: (name: string) => void
}
let person: Person = {
greet(name) {
console.log(name)
}
}
axios({ ... })
时,如果发送 GET 请求,method 属性就可以省略?
来表示type Config = {
url: string
method?: string
}
function myAxios(config: Config) {
console.log(config)
}
当一个对象类型被多次使用时,一般会使用接口(interface
)来描述对象的类型,达到复用的目的
interface
关键字来声明接口I
开头interface IPerson {
name: string
age: number
sayHi(): void
}
let person: IPerson = {
name: 'jack',
age: 19,
sayHi() {}
}
interface IPerson {
name: string
age: number
sayHi(): void
}
// 为对象类型创建类型别名
type IPerson = {
name: string
age: number
sayHi(): void
}
// 为联合类型创建类型别名
type NumStr = number | string
interface Point2D { x: number; y: number }
interface Point3D { x: number; y: number; z: number }
interface Point2D { x: number; y: number }
// 继承 Point2D
interface Point3D extends Point2D {
z: number
}
extends
(继承)关键字实现了接口 Point3D 继承 Point2Dlet position: number[] = [116.2317, 39.5427]
元组 Tuple
let position: [number, number] = [39.5427, 116.2317]
// 变量 age 的类型被自动推断为:number
let age = 18
// 函数返回值的类型被自动推断为:number
function add(num1: number, num2: number) {
return num1 + num2
}
console.log()
document.createElement()
let str1 = 'Hello TS'
const str2 = 'Hello TS'
{ name: 'jack' }
[]
18
20
'abc'
false
function() {}
let str1 = 'Hello TS'
const str2 = 'Hello TS123'
str1 = str2
// str2 = str1 不行
// 使用自定义类型:
type Direction = 'up' | 'down' | 'left' | 'right'
function changeDirection(direction: Direction) {
console.log(direction)
}
// 调用函数时,会有类型提示:
changeDirection('up')
// 创建枚举
enum Direction { Up, Down, Left, Right }
// 使用枚举类型
function changeDirection(direction: Direction) {
console.log(direction)
}
// 调用函数时,需要应该传入:枚举 Direction 成员的任意一个
// 类似于 JS 中的对象,直接通过 点(.)语法 访问枚举的成员
changeDirection(Direction.Up)
enum
关键字定义枚举,
(逗号)分隔数字枚举
// Down -> 11、Left -> 12、Right -> 13
enum Direction { Up = 10, Down, Left, Right }
enum Direction { Up = 2, Down = 4, Left = 8, Right = 16 }
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}
// 会被编译为以下 JS 代码:
var Direction;
(function (Direction) {
Direction['Up'] = 'UP'
Direction['Down'] = 'DOWN'
Direction['Left'] = 'LEFT'
Direction['Right'] = 'RIGHT'
})(Direction || Direction = {})
let obj: any = { x: 0 }
obj.bar = 100
obj()
const n: number = obj
在项目开发中,尽量少用any类型
有时候你会比 TS 更加明确一个值的类型,此时,可以使用类型断言来指定更具体的类型。 比如,
const aLink = document.getElementById('link')
const aLink = document.getElementById('link') as HTMLAnchorElement
<>
语法,这种语法形式不常用知道即可:// 该语法,知道即可:在react的jsx中使用会报错
const aLink = <HTMLAnchorElement>document.getElementById('link')
技巧:在浏览器控制台,通过 __proto__
获取 DOM 元素的类型
console.log(typeof 'Hello world') // ?
let p = { x: 1, y: 2 }
function formatPoint(point: { x: number; y: number }) {}
formatPoint(p)
function formatPoint(point: typeof p) {}
typeof
操作符来获取变量 p 的类型,结果与第一种(对象字面量形式的类型)相同