TypeScript 是 JavaScript 的一个超集,它添加了静态类型和其他一些功能,使得代码更易于维护和阅读。以下是 TypeScript 的一些主要特点:
- 静态类型
- 接口
- 泛型
- 枚举
- 类型推断
- 类
- 模块
- 类型别名
- 类型守卫
- 声明文件
TypeScript的优势
- 编译时静态类型检查
- 自动提示更清晰明确
- 引入泛型和一系列的TS特有类型
- 强大的
t.ts
声明文件 - 轻松编译成JS文件
- 灵活性高
类型注解和类型推导
let data: number = 0
// data = '222' // 提示报错
let money = "abc" // 类型系统会推断为string
// money = 1111 // 提示报错
-
区别:
- 类型注解:定义时固化类型
- 类型推导:根据值的类型反过来推导类型
-
优点
TS编译和编译优化
-
正常情况下,TS检测变量,会把所有变量都当成全局变量检测
- 解决方法
- 在代码结尾添加
export {}
作用域- 表明当前变量作用域是当前文件
- 在代码结尾添加
- 解决方法
-
TS代码默认不能被正常运行
- 不能使用
node
直接运行 - 浏览器也不能直接加载运行
- 不能使用
-
运行方式
|------------| tsc |------------|
| Javascript |----------------> | Typescript |
|------------| |------------|
TS的24种类型
-
基本类型
- number
- string
- boolean
- symbol
- null
- undefined
-
根类型
- Object
-
对象类型
- Array
- object
- function
-
特殊类型
- any
- unknown
- never
- void
- tuple
- 可变元组
-
合成类型
- 联合类型
- 交叉类型
-
字面量数据类型
// 联合数据类型
let dat: string | number = 'a'
// 交叉类型,可以合并在一起
type Obj1 = { username: sring}
type Obj2 = { age: number}
let obj1:Obj1 = { username: 'aaa' }
let obj2:Obj2 = { age: 22 }
let obj3: Obj1 & Obj2 = { username: 'ww', age: 3 }
any
和unknown
的区别和应用场景
-
相同点
any
和unknown
可以是任何类的父类- 所以任何类型的变量都可以赋值给
any
类型或unknown
类型的变量
- 所以任何类型的变量都可以赋值给
-
不同点
any
也可以是任何类的子类,但unknown
不可以- 所以
any
类型的变量都可以赋值给其它类型的变量
- 所以
- 不能拿
unknown
类型的变量来获取任何属性和方法,但any
类型的变量可以获取任意名称的属性和任意名称的方法
-
any
比较典型的应用场景- 自定义守卫
- 需要进行
as any
类型断言的场景
-
unknown
的应用场景- 一般用作函数参数
- 用来接受任意类型的变量实参
- 但在函数内部只用于再次传递或输出结果,不获取属性的场景
- 一般用作函数参数
export function isRef(r: any): r is Ref {
return Boolearn(r && r.__v_isRef === true) // any类型的r参数在函数内部获取属性
}
function ref(value?: unknown) {
return createRef(value) // 函数内部只用于再次传递,不获取属性
}
接口和应用场景
-
接口
- 另一种定义对象类型的类型
-
接口的应用场景
- 一些第三方包或者架构底层源码中有大量的接口类型
- 提供方法的对象类型的参数时使用
- 为多个同类别的类提供统一的方法和属性声明
-
如何定义接口
- 接口中的类型的方法都只有声明,没有实现
-
接口继承
- 新的接口知识在原来的接口继承上增加了一些属性或方法
-
接口的可索引签名
- 用来接收不确定字段名
interface Product {
name: string
price: number
account: number
[x: string]: any // string, 支持各种类型,如果是number,则只支持number
}
let p: Product = {
name: "1",
price: 1,
account: 1,
describe: 'ok', // Ok
[Symbol('stockno')]: 33, // Ok
100: 1, // Ok
true: "o" // Ok
}
索引访问类型
- keyof
- keyof 类型
- 获取类型的所有属性名组成的联合类型
- keyof 类型
type symid = Symbol('productno')
interface Product {
[symid]: string
name: string
price: number
account: number
buy(): string
}
type A = Product['price']
type B = Product['price' | 'name']
type S = Product[typeof symid]
type PKeys = keyof Product // 获取所有属性名组成的联合类型
// "name" | "price" | "account" | "buy" | typeof symid
- typeof
- typeof variable
- 获取变量对应的类型
- typeof variable
void 类型
null 和 undefined
-
如果希望没变量没赋值还不报错,使用
undefined
的联合类型 -
?
会将字段的类型变成字段类型和undefined
的联合类型 -
哪些类型可以接受
undefined
- any
- unknown
- undefined
-
那些类型可以接受
null
- any
- unknown
- null
typeof null // {}
typeof undefined // undefined
let str: string | undefined
console.log("str: ", str)
function func(data?: string) {
data?.toString()
data!.toString() // ! 会忽略 undefined
if (data) {
data.toString()
}
}
常见取值错误
let obj = { username: 'wu', age: 22 }
let username = "username" // 出问题
const username = "username"
let u = obj[username]
TS函数与JS函数
TS函数类型解构
type TypStuobj = { username: string, age: number, phone: string }
function info(stuInfo: TypStuobj) {
console.log("name: ", stuInfo.username, "age: ", stuInfo.age)
}
// 只需要username和phone,解构
function info({ username, phone }: TypStuobj) {
console.log("name: ", stuInfo.username, "age: ", stuInfo.age)
}
interface
和type
的区别
-
type和interface类似
- 都用来定义类型
-
区别
- 定义类型的范围不同
interface
- 定义对象类型
- 接口当名字的函数类型
type
- 可以定义任何类型
- 基础类型
- 联合类型
- 交叉类型
- 元组
- 可以定义任何类型
- 接口可以
extends
一个或多个接口或实现一个或多个接口
- 也可以继承
type
type
类型没有继承功能interface
可以继承type
,但很少见
- 用
type
交叉类型&
- 可让类型中的成员合并成一个新的
type
类型 interface
不能交叉合并
元组
- 满足下面3点的数组就是元组
- 在定义的时候每个元素的类型都确定
- 元素值的数据类型必须是当前元素定义的类型
- 元组值的个数必须和定义时个数相同
数组和数组元素怎样同时为只读
const account = [1, 2, 3] as const // 约束每个元素也是常量
可变元组
// 可变元组
let customers: [string, number, string, ...any[]] = ['1', 1, '1', true]
// 可变元组解构,可以解构任意个数
let [custname, age, ...rest]: [string, number, string, ...any[]] = ['1', 1, '1', true]
// 元组标签
let [custname, age, address, ...rest]: [custname:string, age:number, address:string, ...rest:...any[]] = ['1', 1, '1', true]
类与静态属性
- 类
- 拥有相同属性和方法的一些列对象的集合
类型守卫与类型转换
A as Type
绕过类型检查
- 类型断言:
A as B
- 类型转换:
<type>
typeof
的局限性与替代方案
-
typeof
- 用来检测一个变量或类型
-
局限性
// 类型判断
const arr = [30, 30]
console.log(typeof arr) // object
const set = new Set()
console.log(typeof set) // object
const map = new Map()
consolo.log(typeof map) // object
// 替代方法
console.log(Object.prototype.toString.call(arr)) // [object Array]
console.log(Object.prototype.toString.call(set)) // [object Set]
console.log(Object.prjjototype.toString.call(map)) // [object Map]
// 但不能精准判断类属性的名字
// 实例判断: <instance> instanceof Type
// `in`
// - 属性或方法的判断
// - <attr>/<method> in variable
// 字面量相等判断
// - `===`
自定义类型守卫
function isClass(形参: 参数类型): 形参 is 类型 {
return type or false
}
泛型
-
泛型
- 定义时不明确使用时必须确定某种具体数据的数据类型
- 编译器期间对数据类型检查的数据类型
-
泛型约束
<T extends Type>
<T extends Type, K extends keyof T>
<T extends Type, K extends keyof T ? K: never>
-
反向为泛型赋值
函数重载
- 重载签名: 只有声明
- 实现签名: 实现多个类型组成的联合类型(参数类型/返回值类型)
泛型工厂函数类型
new
表示构造函数,不是创建- 类名指向类的构造函数的空间
export default class CommercialBank {
address: string = "bj"
name: string = 'zhangs'
count: number
constructor(name: string, address: string) {
this.name = name
this.address = address
}
loan(): void {
console.log(this.name + ' 银行贷款')
}
}
type ConstructorTyp = new (...args: any) => any
interface ConstructorInter {
new (...args: any): any
}
//function createFactoryConstructor(constructorTyp: new (...args: any) => CommercialBank) {
// function createFactoryConstructor(constructorTyp: new (...args: any) => any) {
function createFactoryConstructor(constructorTyp: ConstructorInter) {
console.log(constructorTyp.name + '被创建...')
new constructorTyp()
}
// 调用 ClassName is constructor
createFactoryConstructor(CommercialBank)
交叉类型
type O1 = { a: string; b: number }
type O2 = { a: string; b: number }
type O3 = O1 & O2
let o3: O3 = {a: 'd', b: 1, c: 'c', d: 2}
通用交叉方法
function cross<T extends object, U extends object>(obj1: T, obj2: U): T & U {
const combine = {} as T & U
for (let k in obj1) (combine as any)[k] = obj1[k]
for (let k in obj2) (combine as any)[k] = obj2[k]
}
let o1: O1 = { a: 'a', b: 3}
let o2: O2 = { c: 'c', d: 4}
console.log(cross(o1, o2))