TS 总结
3.TS 中 any 、void 、never、 unknwon 的区别?
6.TS中 type 和 interface有什么区别?如何选择?
优点
-
-
- TS是静态类型
- TS有类型错误检查,而且是在编译时就报错(而非运行时)
- TS有智能提示,提高开发效率和稳定性
-
缺点
-
-
- 有一定学习成本
- 某些情况,类型定义过于混乱,可读性差
-
适应场景
-
-
- 大型项目,业务复杂,维护人员多
- 逻辑性比较强的代码,需要类型更稳固
- 组内至少有一个懂TS 的技术leader负责来把控代码规范
-
-
- boolean (布尔类型)
- number (数字类型)
- string (字符串类型)
- symbol
- undefined (未定义)
- 只能赋值本身和void类型
let c: undefined = undefined; c = void(0); c = undefined;
- 只能赋值本身和void类型
- null (空值,无值)
- 只能复制本身
let value:null = null; value = null
- 只能复制本身
- void (没有类型)
- 配合函数使用,表示该方法没有返回值
function hello():void{ alert('hello')
// 或者返回 undefined
// return undefined }
- 配合函数使用,表示该方法没有返回值
- any (任意类型)
- 使用any类型时,不会做类型检查,甚至可以调用起属性和方法。和JS一样。
let num:any = 123 num = '24' num = true num.a
- 定义存储各种类型数据的数组时
let arrList: any[] = [1,false,'34'] arrList[1] = 100
- 使用any类型时,不会做类型检查,甚至可以调用起属性和方法。和JS一样。
- never (永远不存在的类型)
- 一般用在总会抛出异常或者无限循环中
// 死循环,无限循环 function f(): never{ while(true){} } // 抛出异常 // 返回 never 的函数必须存在无法到达的终点 function err(message: string): never{ throw new Error(message) }
- 一般用在总会抛出异常或者无限循环中
- unknown (未知类型)
- 当不确定变量的具体类型时,可以使用unknown
- unkwon 是安全的 any类型
- unknwon 需要通过类型断言或类型检查才能进行操作
let b: unknown; b = true; b = '123'; console.log(b.length) // 错误 console.log((b as string).length) //需要断言
- array(数组类型)
- 数组array 是单一类型的
- 两种写法:
- 类型后面 + []
let arr:string[] = ['1']; arr = ['2','3']
- 使用数组泛型,Array<类型>
let arr:Array<number>; arr = [1,2]
- 类型后面 + []
- tuple (元组类型)
- 已知元素数量和类型,各个元素的类型可以不相同
- 赋值时,类型、位置、个数需要都要和定义的一直
let tupleArr:[number,string,boolean]; tupleArr = [12,'34',true] // 0k tupleArr = [12,'34'] // 错误的
- enum (枚举类型)
- 对JS 标准数据类型的一个补充
enum Color { Red, Green, Blue } let c: Color = Color.Red
- 对JS 标准数据类型的一个补充
- object (对象类型)
let obj:object obj = {name:'',age:10}
写法:变量名后加‘:’ 加上类型 的形式
const s:sting = 'a'
let n:number;
n = 100
TS 可以进行类型断言,如果变量直接赋值的情况,可以去掉类型(TS 自己会根据值去判断类型):即
const s = 'a'
3、TS 中 any 、void 、never、 unknwon 的区别?
- any 是任意类型,不做类型检查,比较危险
- unknown 是未知类型,和any 类似,但比any更加安全。因为unknown 会进行类型检查,使用时需要使用as断言
- void 是没有类型,用在一个函数的返回值,比如一个函数没有返回值,或者返回undefined
- never 是永远不存的类型,用在函数返回,比如死循环 或者 抛出异常错误的函数,该函数永远也无法达到终点
- public (公共)
- 可以自由的访问类程序里通过public定义的属性和方法。
- private (私有)
- private 定义的属性和方法,只能够在该类中的内部进行访问。继承的子类和实例对象不能访问到
- protected (受保护)
- 通过protected 定义的属性和方法 ,可以在该类内部和继承的子类中访问。但不能被实例对象访问
class Parent { public name:string private friend: string = 'lili' protected age:number constructor (name: string, age: number){ this.name = name; this.age = age } public hello(){ } private hi(){ } protected getName(){ } } class Child extends Parent { constructor(name: string ,age: number){ super(name,age) } getInfo(){ console.log(child1.name) //公共的方法,可以被继承类访问 console.log(this.hello()) //公共的方法,可以被继承类访问 console.log(this.age)//受保护的属性,可以被继承类访问 console.log(this.getName()) //受保护的方法,可以被继承类访问 console.log(this.hi()) //私有方法 不能不继承类访问 console.log(this.friend) //私有属性 不能被继承类访问 } } const child1 = new Child('xioaming',20) console.log(child1.name) //公共的方法,可以被实例对象访问 console.log(child1.hello()) //公共的方法,可以实例对象访问 console.log(child1.age)//受保护的属性,可以实例对象访问 console.log(child1.getName()) //受保护的方法,可以被实例对象访问 console.log(child1.hi()) //私有方法 不能被实例对象访问 console.log(child1.friend) //私有属性 不能被实例对象访问
TS中可以通过构造函数的参数直接定义属性:
class Parent { constructor(public name:string,protected age:number, private friend = 'lili'){ } } //等同于下面的代码 class Parent { public name:string private friend: string = 'lili' protected age:number constructor (name: string, age: number){ this.name = name; this.age = age } }
面向对象的三要素: 1.继承 2. 封装 (保证私有属性不被访问)3. 多态(函数重载)
- # 和 private 在类中都是定义私有属性
class Parent { private name:string // 私有属性 #age:number // 私有属性 constructor(name:string,age:number){ this.name = name this.#age = age } getInfo(){ console.log(this.name) //获取私有属性 console.log(this.#age) //获取私有属性 } }
- #定义的属性,不能再构造函数参数中定义;而private可以在构造函数参数中直接定义
class Parent { #age:number // 私有属性 constructor(private name:string,age:number){ this.#age = age } getInfo(){ console.log(this.name) console.log(this.#age) } }
- private定义的私有属性,只是在编译时检查会给出警告,外部代码可以通过一些编译警告来访问这些成员(eg: 通过 as any进行断言 )。但在编译后的JS代码中,这些成员实际上是公开的。
- #定义的私有属性是真正的私有属性,只能在类的内部访问。外部代码无法访问这些成员。即使编译后的JavaScript代码仍也无法通过常规方式访问到
- # 适用于需要严格保护类成员不被访问的场景,确保封装性和安全性
- private 使用于在编译时进行检查,但不需要严格运行时进行隔离的场景。
6、TS中 type 和 interface有什么区别?如何选择?
- 类型别名 type:
- 类型别名:是用来给一个类型起个新名字的
- type可以支持多种类型定义:包括基本类型、对象类型、联合类型、交叉类型、元组等等
type Name = string type List = Array<number>
//对象 type UserType ={ name: string age: number getName: ()=> string }
// 函数
type SetName = (name: string)=> void
接口 interface:
-
- 接口interface:是一种用来描述对象或函数的东西
// 对象
interface User { name: string age: number getName: ()=> string }
// 函数
interface setName {
(name:string):void
}
- 接口interface:是一种用来描述对象或函数的东西
二者的相同点:
-
- 都可以描述一个对象结构
- 都可以被 类class来实现接口,必须使用 implements 关键字
//interface 定义的类型
interface User {name: stringage: numbergetName: ()=> string}//type 定义的类型type UserType ={name: stringage: numbergetName: ()=> string}// Person 类实现了 User 接口class Person implements User{ name: string; age: number getName: () => string } // Person 类实现了 UserType 接口 class Person implements UserType{ name: string; age: number getName: () => string }
- 都可以扩展属性
- interface 扩展interface
interface User { name: string age: number } interface School { schoolName:string schoolDress:string } // User2 接口 扩展了 User 和 School interface User2 extends User,School{ salary:number } const user: User2= { name: '23', age:20, schoolName:'ww', schoolDress:'xxxxx', salary: 30 }
- interface 扩展 type
type User = { name: string age: number } //User 接口 扩展了User interface User2 extends User{ salary:number } const user: User2= { name: '23', age:20, salary: 30 }
- type 扩展type
type User = { name: string age: number } type School = { schoolName:string schoolDress:string } // User2 类型 扩展 // type 类型的扩展使用交叉类型(&) type User2 = User & School & { salary:number } const user: User2= { name: '23', age:20, schoolName:'x', schoolDress:'xxx', salary: 30 }
- type 扩展 interface
interface User { name: string age: number } type User2 = User & { salary:number } const user: User2= { name: '23', age:20, salary: 30 }
- interface 扩展interface
区 别:
-
- type 可以是基础类型、联合类型、交叉类型;interface 不可以
- typeof 可以通过typeof复制;interface 不可以
- interface 可以合并声明,type不可以重复定义
interface User { name: string } interface User { age: number } // interface 会合并声明 const user: User= { name: '23', age:20 }
//type 不能重复定义 type User = { name:string } // 会报标识符 User 重复 type User = { age:number }
type 和 interface 如何选择?
-
- TS的初衷:type 定义类型关系;interface 定义数据结构
- 但实际使用时,我们很多时候模糊不清
- 个人建议:优先使用 interface,再使用type
是什么?
-
- 泛型指定义函数、类或接口时,不预先定义好具体类型,而是使用类型参数作为占位符
- 这些占位符在函数、类或者接口被实例化或调用时,再被指定类型
泛型的具体应用场景:
-
- 函数
- 简单例子:
function fn<T>(arg: T): T{ return arg } fn<string>('hello') fn<number>(100)
-
可以一次定义多个类型参数
function fn3<T,U>(tupe:[T,U]):[U,T]{ return [tupe[1],tupe[0]] } fn3<number,string>([100,'23']) fn3<boolean,string>([true,'23'])
- 简单例子:
- 类
- 基本使用
class Person<T> { name:T constructor(name:T){ this.name = name } getName(): T { return this.name } } new Person('xxxx') new Person(12)
- 可以使用<T extends xx> 方式约束泛型
type Params = string | number class Person<T extends Params> { name:T constructor(name:T){ this.name = name } getName(): T { return this.name } }
- 基本使用
- 接口
-
interface User<T,U> { id: T name: string age: number sex: U } const u: User<string,number> = { id: '1', name: '33', age: 24, sex: 0 } const u2: User<string,string> = { id: '1', name: '33', age: 24, sex: '女' }
-
- 函数
- 交叉类型
- 通过 “&” 将多个类型合并为一个类型,即 U1 & U2&U3。可以获取所有属性
interface U1 { name: string city: string } interface U2 { name: string age: number } const user1:UserType = { name:''", city:'', age:10 }
- 缺陷:属性类型不能冲突。如果属性类型冲突,那该冲突属性类型会被标记为never
- 交叉类型只能合并对象类型,基础类型无法交叉,会返回never
- 应用场景:合并多个对象的属性
- 通过 “&” 将多个类型合并为一个类型,即 U1 & U2&U3。可以获取所有属性
- 联合类型
- 联合多个类型 T1|T2|T3,一种“或”的关系
- 联合类型可以是基础类型也可以是对象类型
interface U1 { name: string city: string } interface U2 { name: number age: number } function fn(): U1 | U2{ return { name:'', //name:1, age:10, city:'xx' } } type T = string | number
- ?
- 可选
- 在函数中,可选参数必须放到后面。
type User ={ name: string age?:number //可选参数 city:string } const u:User = {name: '',city:'ss'} console.log(u) //函数中的可选参数必须放到后面 function fn(a:number,b?:string){ console.log(a,b) } fn(12)
- ?.
- 可选链操作符。
- 用在访问对象属性时进行安全检查,如果对象链中的任何一部分为 null或者undefined,则直接返回null或者undefined。
// 如果 info 不存在,则直接返回undefined,而不会报错 const city = user?.info?.city
- 使用场景:开发者不确定对象是否为空,进行安全的属性访问,避免运行时出错
- ??
- 空值合并云算符,当左侧是null或者 undefined 时会返右侧
- 和 || 有点像,|| 是左侧为假时返回右侧
const user = { name:'zhangsan', index: 0 } const n1 = user.name ?? '暂无姓名' // 'zhangsan' const n2 = user.name || '暂无姓名' // 'zhangsan' const i1 = user.id ?? '暂无 id' // 0 const i2 = user.id || '暂无 id' // 暂无 id
- !
- 非空断言,告诉编译器某个值就对不会是null或者 undefined
function fn1(a?:string){ return a!.length }
- 使用场景:开发者明确某个值不会是 null或 undefined,希望TS忽略空值检查的场景
- 非空断言,告诉编译器某个值就对不会是null或者 undefined
- _
- 数字分隔符,用来提高代码可读性
- 适用场景: 货币、手机等等
const million = 1_000_000 //1000000 const phone = 151_1234_2345 //15112342345
- &
- 交叉类型
- |
- 联合类型
- #
- 类中定义的私有属性
- Partial<T>
- 将类型T中的所有属性变为可选属性
interface User { name:string age: number city?:string } const user1: Partial<User> = { name:'' }
- 将类型T中的所有属性变为可选属性
- Required<T>
- 将类型T中的所有属性变为必选属性。相当于把可选属性全部去掉
interface User { name:string age: number city?:string } const user1: Required<User> = { name:'', age:10, city:'北京' // city 也必选 }
- 将类型T中的所有属性变为必选属性。相当于把可选属性全部去掉
- Pick<T,K>
- 从类型T中选择一组属性。
- K是有一个属性名或者属性名的联合类型
interface User { name:string age: number city:string } // 只包含name 属性 const user1: Pick<User,'name'> = { name:'' } //只包含 name 和 age 属性 const user2: Pick<User,'name' | 'age'> = { name:'', age:18 }
- K是有一个属性名或者属性名的联合类型
- 从类型T中选择一组属性。
- Omit<T,'属性名'>
- 从类型T中排出某些属性
interface User { name:string age: number city?:string } //去掉name 属性 const user1: Omit<User,'name'> = { age:10, city:'' //可选的 } //去掉 name 和 age 属性 const user2: Omit<User,'name' | 'age'> = { city:'222' //可选的 }
- 从类型T中排出某些属性
- Exclude<T>
- Extract<T>
- Readyonly<T>
- 将T中的所有属性变为只读,不允许修改
interface User { name:string age: number city?:string } type User2 = Readonly<User> const user: User2 = { name:'', age:10, city:'北京' //可选的 } //user.name = 'lisi' 不能不修改
-
和类型属性前面加只读属性一样
interface User { readonly name:string readonly age: number readonly city?:string }
-
const 和 readonly区别
-
const 定义的变量为只读
-
readonly 定义只读属性
-
- 将T中的所有属性变为只读,不允许修改
-
- 新建一个xx.d.ts 声明文件
- xx.d.ts文件中 用declare 声明 Window 接口,接口中可以定义我们自己的属性
// xx.d.ts 声明文件 declare interface Window { // Window 首字母大写 test: string }
-
使用时,也不需要引入,直接用即可
window.test = "Hello, World!"; console.log(window.test); // 输出: Hello, World!
-
- 现在很多插件都是直接用TS开发的,本身包好了类型定义
- 常见的第三方插件,都有‘@types/xxx’包,安装即可使用
- 其他的,可以通过 declare moudule xxxx 定义类型
// xxx.d.ts 文件中声明 declare module 'someModule' {
// prop 是 someModule 中需要的参数 function fn(prop:number): void { } export {fn} } -
在使用的地方 直接import 导入函数即可使用
import {fn} from 'someModule' fn(200)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!