笔记整理-主要参考:vue3.chengpeiquan.com/
前言
-
官网:
- TS入门教程
- 开源社区的流行趋势:octoverse.github.com/#top-langua…
- 前端语言(如TypeScript、JavaScript等)Flow.js:flow.org/;https://gi…
-
TS编译环境
- 全局安装:
npm install typescript -gtsc --version
- 全局安装:
-
使用ts-node
- 安装ts-node:
npm install ts-node -g - ts需依赖tslib和@type/node两个包:
npm install tslib @types/node -g - 用ts-node运行TS代码:
ts-node math.ts
- 安装ts-node:
选择类型系统(TypeScript)原因
- JavaScript过于灵活,没有类型约束,因类型变化会导致一些bug(程序运行时才会被发现,容易引起生产事故),TypeScript可避免以上情况
类型定义
原始数据类型
- string字符串、number数值、boolean布尔值、bigint大整数、symbol符号、null不存在、undefined未定义
- TypeScript与JavaScript原始数据类型唯一区别:TS是小写,JS是大写
- 小驼峰命名法
//定义原始数据类型
const str:string = 'Hello'
const numTotal:number = 12
const isFlag:boolean = true
//TS推导他们的类型(不会报错)
const str1='Hello'
数组(引用类型)Array
- 写法:
原始数据类型[]或Array<原始数据类型>
-
说明:
Array<原始数据类型>是基于TS的泛型Array<T>- 是最接近原始数据类型的引用类型
- 若不写明类型,会根据值推导;没有值会认为是any[]或nerve类型
// 字符串数组
const strs: string[] = ['Hello World', 'Hi World']
// 数值数组
const nums: number[] = [1, 2, 3]
// 布尔值数组
const bools: boolean[] = [true, true, false]
// 这种有初始项目的数组, TS 也会帮推导它们的类型
const strs = ['Hello World', 'Hi World']
const nums = [1, 2, 3]
const bools = [true, true, false]
// 这个时候会认为是 any[] 或者 never[] 类型
const nums = []
// 这个时候再 push 一个 number 数据进去,也不会使其成为 number[]
nums.push(1)
对象(接口-引用类型)Object
-
定义方式:首字母大写(大驼峰命名)
interface UserInfo{},无等号,比较常用type UserInfo={},有等号
-
定义对象中属性的数据类型
-
定义对象中属性的数据类型,声明对象时需要与之对应(不要缺少属性)
-
可选的接口属性
-
可调用自身接口的属性
-
接口的继承(extends)
- 继承别的接口所有属性
- 继承别的接口部分属性(别的接口,不被继承的属性 应该是可选的)
- 继承时,删除多余的属性:
Omit<对象名,'属性1'|'属性2'>
-
//1. 正常的代码
// 定义用户对象的类型
interface UserItem {
name: string
age: number
}
// 在声明变量的时候将其关联到类型上
const petter: UserItem = {
name: 'Petter',
age: 20,
}
//2. 错误的代码
// 注意!这是一段会报错的代码
interface UserItem {
name: string
age: number
}
const petter: UserItem = {
name: 'Petter',
}
//3. 正常的代码
interface UserItem {
name: string
// 这个属性变成了可选
age?: number
}
const petter: UserItem = {
name: 'Petter',
}
//4. 调用自身的接口属性
interface UserItem {
name: string
age: number
enjoyFoods: string[]
// 这个属性引用了本身的类型
friendList: UserItem[]
}
const petter: UserItem = {
name: 'Petter',
age: 18,
enjoyFoods: ['rice', 'noodle', 'pizza'],
friendList: [
{
name: 'Marry',
age: 16,
enjoyFoods: ['pizza', 'ice cream'],
friendList: [],
},
{
name: 'Tom',
age: 20,
enjoyFoods: ['chicken', 'cake'],
friendList: [],
}
],
}
//5. 接口的继承
//5.1 继承所有属性
interface UserItem {
name: string
age: number
enjoyFoods: string[]
friendList: UserItem[]
}
// 这里继承了 UserItem 的所有属性类型,并追加了一个权限等级属性
interface Admin extends UserItem {
permissionLevel: number
}
const admin: Admin = {
name: 'Petter',
age: 18,
enjoyFoods: ['rice', 'noodle', 'pizza'],
friendList: [
{
name: 'Marry',
age: 16,
enjoyFoods: ['pizza', 'ice cream'],
friendList: [],
},
{
name: 'Tom',
age: 20,
enjoyFoods: ['chicken', 'cake'],
friendList: [],
}
],
permissionLevel: 1,
}
//5.2继承部分属性
interface UserItem {
name: string
age: number
enjoyFoods: string[]
friendList?: UserItem[]
}
// 这里在继承 UserItem 类型的时候,删除了两个多余的属性
interface Admin extends Omit<UserItem, 'enjoyFoods' | 'friendList'> {
permissionLevel: number
}
// 现在的 admin 就非常精简了
const admin: Admin = {
name: 'Petter',
age: 18,
permissionLevel: 1,
}
Symbol类型
- 场景:一个对象中可以有两个相同属性名
类
- 特点:是JS ES6推出的一个概念,通过class定义一个对象模板
-
定义类
class User{}- 类与类之间可继承
- 一个接口可继承类
- 继承时,删除多余的属性:Omit<类名,'属性1'|'属性2'|'方法名'>
//1. 定义一个类
class User {
// constructor 上的数据需要先这样定好类型
name: string
// 入参也要定义类型
constructor(userName: string) {
this.name = userName
}
getName() {
console.log(this.name)
}
}
// 通过 new 这个类得到的变量,它的类型就是这个类
const petter: User = new User('Petter')
petter.getName() // Petter
//2. 类与类之间的继承
// 这是一个基础类
class UserBase {
name: string
constructor(userName: string) {
this.name = userName
}
}
// 这是另外一个类,继承自基础类
class User extends UserBase {
getName() {
console.log(this.name)
}
}
// 这个变量拥有上面两个类的所有属性和方法
const petter: User = new User('Petter')
petter.getName()
//3. 一个接口继承类
// 这是一个类
class UserBase {
name: string
constructor(userName: string) {
this.name = userName
}
}
// 这是一个接口,可以继承自类
interface User extends UserBase {
age: number
}
//接口继承类的时候也可以去掉类上面的方法
interface User extends Omit<UserBase, 'getName'> {
age: number
}
// 这样这个变量就必须同时存在两个属性
const petter: User = {
name: 'Petter',
age: 18,
}
联合类型
- 特点:一个变量可能出现多种类型,可以用联合类型定义
- 定义:用
|分隔类型,一般使用模板字符串(反引号)
// 可以在 demo 里运行这段代码
function counter(count: number | string) {
console.log(`The current count is: ${count}.`)
}
// 不论传数值还是字符串,都可以达到我们的目的
counter(1) // The current count is: 1.
counter('2') // The current count is: 2.
-
场景:
- 通过路由实例判断是否符合要求的页面(vue的路由在不同数据结构里也有不同类型)
- 涉及子组件或DOM操作,当还没有渲染出来获取的是null,渲染后才拿到组件或DOM结构
// 注意:这不是完整的代码,只是一个使用场景示例
import type { RouteRecordRaw, RouteLocationNormalizedLoaded } from 'vue-router'
function isArticle(
route: RouteRecordRaw | RouteLocationNormalizedLoaded
): boolean {
// ...
}
// querySelector 拿不到 DOM 的时候返回 null
const ele: HTMLElement | null = document.querySelector('.main')
任意值(any类型)
- 特点:入参可以是多种不同类型
- 注意点:一旦使用了 any ,代码里的逻辑请务必考虑多种情况进行判断或者处理兼容。
// 这段代码在 TS 里运行会报错
function getFirstWord(msg) {
console.log(msg.split(' ')[0])
}
getFirstWord('Hello World')
getFirstWord(123)
//将隐式的改为显式
// 这里的入参显式指定了 any
function getFirstWord(msg: any) {
// 这里使用了 String 来避免程序报错
console.log(String(msg).split(' ')[0])
}
getFirstWord('Hello World')
getFirstWord(123)
unknown类型
- 特点:用于描述不确定变量
void类型
- 特点:一个函数没有返回值,其返回值是void类型
never类型
- 特点:表示函数永远不会发生值的类型
- 场景:函数是个死循环或抛出一个异常
tuple类型
- 特点:元组中每个元素都有自己特性的类型,根据索引值获取的值可确定对应类型
- 场景:tuple作为返回的值
函数
- 特点:是JS中重要成员之一,所有功能的实现都是基于函数
函数的基本写法
// 注意:这是 TypeScript 代码
// 写法一:函数声明
function sum1(x: number, y: number): number {
return x + y
}
// 写法二:函数表达式
const sum2 = function(x: number, y: number): number {
return x + y
}
// 写法三:箭头函数
const sum3 = (x: number, y: number): number => x + y
// 写法四:对象上的方法
const obj = {
sum4(x: number, y: number): number {
return x + y
}
}
// 还有很多……
函数的可选参数
- 注意点:可选参数必须在必传参数的后面
- 补充:可选类型可做 类型 和undefined的联合类型
// 注意 isDouble 这个入参后面有个 ? 号,表示可选
function sum(x: number, y: number, isDouble?: boolean): number {
return isDouble ? (x + y) * 2 : x + y
}
// 这样传参都不会报错,因为第三个参数是可选的
sum(1, 2) // 3
sum(1, 2, true) // 6
无返回值的函数
- 特点:无返回值的函数用void定义(如表单验证)
-
注意点:
- void和null/undefined不可混用
- 返回值类型是null需要return一个null值
// 注意这里的返回值类型
function sayHi(name: string): void {
console.log(`Hi, ${name}!`)
}
sayHi('Petter') // Hi, Petter!
// 只有返回 null 值才能定义返回类型为 null
function sayHi(name: string): null {
console.log(`Hi, ${name}!`)
return null
}
function sayHi(name: string): void {
// 这里判断参数不符合要求则提前终止运行,但它没有返回值
if (!name) return
// 否则正常运行
console.log(`Hi, ${name}!`)
}
异步函数的返回值
- 定义:用Promise定义其返回值,T是指泛型
- 说明:
resolve中是 一个字符串,所以它的返回类型是Promise<string>(假如没有resolve数据,那么就是Promise<void>
// 注意这里的返回值类型
function queryData(): Promise<string> {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Hello World')
}, 3000)
})
}
//没有resolve数据
function queryData(): Promise<void> {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, 3000)
})
}
queryData().then((data) => console.log(data))
函数本身的类型
-
特点:
- 函数名没有指定类型,TS会根据函数体自动推导
-
// 这里的 sum ,确实是没有指定类型 const sum = (x: number, y: number): number => x + y - 完整的函数写法
-
const sum: (x: number, y: number) => number = (x: number, y: number): number => x + y //const sum: (x: number, y: number) => number 是这个函数的名称和类型 //= (x: number, y: number) 这里是指明了函数的入参和类型 //: number => x + y 这里是函数的返回值和类型 - 需显式写出函数的类型:给对象定义方法
-
// 对象的接口 interface Obj { // 上面的方法就需要显式的定义出来 sum: (x: number, y: number) => number } // 声明一个对象 const obj: Obj = { sum(x: number, y: number): number { return x + y } }
函数的重载(明确入参与返回的类型一致)
-
场景
- 当监听单个数据源时,它匹配了类型 1 ,当传入一个数组监听多个数据源时,它匹配了类型 2
- 使用:向声明原始数据类型,在声明引用类型(如,数组)
//1. 函数的基本写法
// 写法一:函数声明
function sum1(x: number, y: number): number {
return x + y
}
// 写法二:函数表达式
const sum2 = function(x: number, y: number): number {
return x + y
}
// 写法三:箭头函数
const sum3 = (x: number, y: number): number => x + y
// 写法四:对象上的方法
const obj = {
sum4(x: number, y: number): number {
return x + y
}
}
// 还有很多……
//2. 函数的可选参数
// 注意 isDouble 这个入参后面有个 ? 号,表示可选
function sum(x: number, y: number, isDouble?: boolean): number {
return isDouble ? (x + y) * 2 : x + y
}
// 这样传参都不会报错,因为第三个参数是可选的
sum(1, 2) // 3
sum(1, 2, true) // 6
//3. 无返回值的函数
// 注意这里的返回值类型
function sayHi(name: string): void {
console.log(`Hi, ${name}!`)
}
sayHi('Petter') // Hi, Petter!
注意点:void 和 null 、 undefined 不可以混用,
若函数返回值类型是 null ,需要 return 一个 null 值
// 只有返回 null 值才能定义返回类型为 null
function sayHi(name: string): null {
console.log(`Hi, ${name}!`)
return null
}
function sayHi(name: string): void {
// 这里判断参数不符合要求则提前终止运行,但它没有返回值
if (!name) return
// 否则正常运行
console.log(`Hi, ${name}!`)
}
//4. 异步函数的返回值
// resolve 一个字符串,所以它的返回类型是 Promise<string>
//(假如没有 resolve 数据,那么就是 Promise<void> )。
// 注意这里的返回值类型
function queryData(): Promise<string> {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Hello World')
}, 3000)
})
}
queryData().then((data) => console.log(data))
//5. 函数本身的类型
//6. 函数的重载
// 这一次用了函数重载
function greet(name: string): string // TS 类型
function greet(name: string[]): string[] // TS 类型
function greet(name: string | string[]) {
if (Array.isArray(name)) {
return name.map((n) => `Welcome, ${n}!`)
}
return `Welcome, ${name}!`
}
// 单个问候语,此时只有一个类型 string
const greeting = greet('Petter')
console.log(greeting) // Welcome, Petter!
// 多个问候语,此时只有一个类型 string[]
const greetings = greet(['Petter', 'Tom', 'Jimmy'])
console.log(greetings)
// [ 'Welcome, Petter!', 'Welcome, Tom!', 'Welcome, Jimmy!' ]
npm包
可能会报...implicitly has an 'any' type类似的错误信息,需在安装原包名前加@types
如:npm install -D @types/md5
类型断言
-
语法
值 as 类型<类型>值
-
常用的场景
- 特点:一个变量使用联合类型,若不显式指出类型可能会导致运行报错
- 重载
// 对单人或者多人打招呼
function greet(name: string | string[]): string | string[] {
if (Array.isArray(name)) {
return name.map((n) => `Welcome, ${n}!`)
}
return `Welcome, ${name}!`
}
// 虽然已知此时应该是 string[]
// 但 TypeScript 还是会认为这是 string | string[]
const greetings = greet(['Petter', 'Tom', 'Jimmy'])
// 会导致无法使用 join 方法(因为 string 类型不具备 join 方法)
const greetingSentence = greetings.join(' ')
console.log(greetingSentence)
-
注意点:类型断言让TS不检查代码(所以只在能够确保代码正确的情况下去使用它)
- 反例子
-
// 原本要求 age 也是必须的属性之一 interface User { name: string age: number } // 但是类型断言过程中,遗漏了 const petter = {} as User petter.name = 'Petter' // TypeScript 依然可以运行下去,但实际上的数据是不完整的 console.log(petter) // { name: 'Petter' }