定义方式 在 TypeScript 中,以下几种方式用于定义对象:
接口(Interface) 常用场景 :
接口用于定义对象的结构,尤其是当对象结构比较复杂、需要复用或者要用于类的类型定义时。
接口非常适合用于定义 API 的数据结构或者复杂的对象类型。
示例 :
1 2 3 4 5 6 7 8 9 10 11 interface Person { name : string ; age : number ; isActive : boolean ; } const person : Person = { name : "Alice" , age : 30 , isActive : true };
类型别名(Type Alias) 常用场景 :
类型别名可以定义对象类型,也可以定义联合类型、交叉类型等。
它非常灵活,适用于定义各种复杂类型,包括对象类型。
示例 :
1 2 3 4 5 6 7 8 9 10 11 type Person = { name : string ; age : number ; isActive : boolean ; }; const person : Person = { name : "Alice" , age : 30 , isActive : true };
类(Class) 常用场景 :
类用于定义具有特定行为和属性的对象。它适用于面向对象编程,尤其是在需要创建多个具有相同结构和行为的对象实例时。
示例 :
1 2 3 4 5 6 7 8 9 class Person { constructor ( public name : string , public age : number , public isActive : boolean ) {}} const person = new Person ("Alice" , 30 , true );
对象字面量(Object Literal) 常用场景 :
对象字面量通常用于简单的数据结构或者临时使用的对象。
对于复杂对象结构或需要复用的类型定义,不建议使用这种方式。
示例 :
1 2 3 4 5 const person : { name : string ; age : number ; isActive : boolean } = { name : "Alice" , age : 30 , isActive : true };
使用场景
接口(Interface) 和 类型别名(Type Alias) 是最常用的定义对象类型的方式,尤其是在大型应用程序或库中。
接口在扩展和复用方面有优势,而类型别名更为灵活,适合定义复杂的联合类型和交叉类型。
类(Class) 在需要封装对象行为时使用较多,例如在面向对象编程中创建多个实例时。
它提供了更多的功能,如构造函数、方法和继承。
对象字面量(Object Literal) 适用于简单场景,通常在局部变量或临时对象的定义中使用较多。
总体来说,接口和类型别名是最常见的选择,特别是在 TypeScript 的类型系统中,它们提供了最好的类型安全和灵活性。
类型定义文件 在 TypeScript 中,如果你有一个类型定义文件(通常是以 .d.ts 为扩展名的文件)。
个人建议类型定义都放在src/assets/types/文件夹下。
全局使用 注意
命名要防止和其他地方冲突。
如果你的类型定义文件不使用模块系统,而是定义了全局类型,你可以直接在 TypeScript 项目的 tsconfig.json 文件中通过 include 来包含这些类型定义文件。
TypeScript 编译器会自动识别并将其应用于整个项目。
我这里把全局使用的类型定义都放在了src/assets/types/下。
全局定义的名称不能重复,全局定义的类型不需要导出(export) 。
1 2 3 4 interface ZUser { name : string ; age : number ; }
我的项目是引用了tsconfig.app.json,所以我在该文件中添加:
1 2 3 { "include" : ["env.d.ts" , "src/**/*" , "src/**/*.vue" , "src/assets/types/*.d.ts" ], }
配置过后,可能需要重启IDEA才能生效。
引用方式使用 user.d.ts
1 2 3 4 export interface User { name : string; age : number; }
使用
1 2 3 4 5 6 import type { ZUser } from '@/assets/types/user' const person : User = { name : 'Alice' , age : 30 };
可选属性和可空属性
可选属性 :使用问号 ?,表示属性可能存在也可能不存在。
可空属性 :在属性类型中添加 null 或 undefined,表示属性可以是指定类型,也可以是 null 或 undefined。
可选属性 1 2 3 4 5 6 7 8 9 10 11 12 13 interface User { name : string ; age ?: number ; } let user1 : User = { name : "Alice" }; let user2 : User = { name : "Bob" , age : 30 };
可空属性 1 2 3 4 5 6 7 8 9 10 11 interface Person { name : string ; age : number |null ; isActive : boolean ; } const person : Person = { name : "Alice" , age : null , isActive : true };
类型断言 类型断言 :
类型断言允许开发者手动指定一个值的类型,有两种语法形式:
1 2 3 4 5 let someValue : any = "this is a string" ;let strLength : number = (someValue as string).length ;let strLength2 : number = (<string>someValue).length ;
JS返回的是对象如果在TS中用,也会报错,我们可以使用类型推断
1 2 3 4 5 6 7 8 const paras = getRequestParas () as { examid : number raid : number userid : number token_key : string token_value : string } let { examid, raid, userid, token_key, token_value } = paras
object类型设置值 1 2 3 4 5 6 7 8 const singleData : object = {};const typedSingleData = singleData as { [key : string ]: any };typedSingleData.name = 'John' ; typedSingleData.age = 30 ; console .log (typedSingleData);
泛型 无泛型 不使用泛型时
1 2 3 4 5 interface ZResult { code : number msg : string obj : any }
单泛型 1 2 3 4 5 6 7 8 9 10 11 12 interface ZResult <T> { code : number msg : string obj : T } interface User { id : number name : string email : string }
这时候可以这样用
1 2 3 4 5 6 7 8 9 10 11 12 const userResult : ZResult <User > = { code : 200 , msg : "User fetched successfully" , obj : { id : 1 , name : "John Doe" , email : "john.doe@example.com" } }; console .log (userResult);
多泛型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 interface ZResult <T, U> { code : number msg : string obj : T extraInfo ?: U } interface User { id : number name : string email : string } interface ExtraInfo { timestamp : Date source : string }
使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const userResult : ZResult <User , ExtraInfo > = { code : 200 , msg : "User fetched successfully" , obj : { id : 1 , name : "John Doe" , email : "john.doe@example.com" }, extraInfo : { timestamp : new Date (), source : "database" } }; console .log (userResult);
扩展接口属性 window是全局唯一的对象,在JS中我们可以方便的添加任何我们想全局访问的属性,但是在TS中不行。
我们需要扩展 Window 接口
添加一个全局的定义文件 globals.d.ts
1 2 3 interface Window { loginUser ?: ZLoginUser }
这样我们就可以在TS中使用了
1 window .loginUser = {name :"张三" }
添加属性 在 TypeScript 中,如果你需要在现有类型中添加或修改属性,有几种常见的方法可以实现。这些方法包括扩展接口、使用类型交叉、和声明合并。
下面详细介绍这些方法:
接口扩展 如果你有一个已有的接口或类型,想要在其基础上添加新属性,可以通过继承或扩展接口来实现。这通常通过声明合并来完成。
示例:
假设你有一个基础接口 Person,现在你想要在其基础上添加一个 age 属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 interface Person { name : string ; } interface Person { age ?: number ; } const person : Person = { name : "Alice" , age : 30 };
使用类型交叉 类型交叉允许你创建一个新类型,它结合了多个类型的属性。这种方式可以用于组合现有类型,或者在类型定义中添加新的属性。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 interface Person { name : string ; } interface AdditionalInfo { age ?: number ; } type ExtendedPerson = Person & AdditionalInfo ;const person : ExtendedPerson = { name : "Bob" , age : 25 };
类型别名与交叉类型 除了使用接口,你也可以使用类型别名和交叉类型来实现类似的效果。这在处理复杂类型或需要快速组合类型时特别有用。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 type Person = { name : string ; }; type AdditionalInfo = { age ?: number ; }; type ExtendedPerson = Person & AdditionalInfo ;const person : ExtendedPerson = { name : "Charlie" , age : 28 };
模块扩展 如果你需要扩展模块的类型(例如,window 对象),可以在模块扩展中定义新的属性。
示例:
1 2 3 4 5 6 declare module "global" { interface Window { myCustomProperty ?: string ; } }
然后在代码中使用扩展后的 window 属性:
1 2 3 window .myCustomProperty = "Hello, TypeScript!" ;console .log (window .myCustomProperty );
在类中添加属性 如果你正在处理类类型,可以直接在类中添加新属性或方法。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Person { name : string ; constructor (name : string ) { this .name = name; } } class ExtendedPerson extends Person { age ?: number ; constructor (name : string , age ?: number ) { super (name); this .age = age; } } const person = new ExtendedPerson ("David" , 40 );console .log (person.name ); console .log (person.age );
总结
接口扩展 :使用声明合并来扩展已有接口。
类型交叉 :使用交叉类型 (&) 来组合多个类型。
模块扩展 :扩展模块的类型以添加新属性。
类扩展 :在类中添加新的属性或方法。