TypeScript - 接口
TypeScript - 接口
Section titled “TypeScript - 接口”TypeScript 中的接口充当一个契约,定义了对象的结构 (shape)。它指定了一个对象 必须 具备的属性和方法的名称及类型,以便被视为与该接口兼容。接口是 TypeScript 结构化类型系统(通常称为“鸭子类型”)的核心特性。
接口只包含成员(属性、方法签名)的声明;它们不提供实现细节。遵循接口结构的责任在于使用该接口的对象或类。
接口纯粹是一种编译时结构。它们不会被编译成 JavaScript,也没有运行时开销。它们的目的完全是为了在开发过程中进行静态类型检查。
使用 interface 关键字,后跟接口名称(根据约定,通常以大写字母 ‘I’ 开头,但这并非强制要求,例如 IPerson 或 Person)。
interface InterfaceName { propertyName: type; optionalProperty?: type; // 可选属性使用 ? readonly readOnlyProperty: type; // 只读属性在初始化后不能修改
methodName(param: type): returnType; // 方法签名 optionalMethod?(param: type): returnType; // 可选方法}示例:用于对象的接口
Section titled “示例:用于对象的接口”interface Person { firstName: string; lastName: string; age?: number; // Optional property readonly id: number; sayHi: () => string; // Method signature}
// 符合 Person 接口的对象const customer: Person = { firstName: "Tom", lastName: "Hanks", id: 101, sayHi: (): string => { return "Hi there"; } // age 是可选的,所以可以省略};
// 另一个符合该接口的对象const employee: Person = { firstName: "Jim", lastName: "Blakes", age: 35, id: 205, sayHi: () => "Hello!!!"};
console.log("Customer Object");console.log(customer.firstName); // Output: Tomconsole.log(customer.lastName); // Output: Hanksconsole.log(customer.sayHi()); // Output: Hi there// customer.id = 102; // 错误:无法赋值给 'id',因为它是一个只读属性。
console.log("\nEmployee Object");console.log(employee.firstName); // Output: Jimconsole.log(employee.age); // Output: 35TypeScript 会检查一个对象是否至少拥有接口定义的属性,并且类型正确。额外属性通常是允许的,除非经过特殊检查(例如,直接赋值对象字面量时)。
用于函数类型的接口
Section titled “用于函数类型的接口”接口也可以描述函数的签名。
interface SearchFunc { (source: string, subString: string): boolean;}
let mySearch: SearchFunc;
mySearch = function(src: string, sub: string): boolean { let result = src.search(sub); return result > -1;}
// 使用箭头函数语法const myOtherSearch: SearchFunc = (source, substring) => { return source.includes(substring);};
console.log(mySearch("hello world", "world")); // Output: trueconsole.log(myOtherSearch("typescript", "java")); // Output: false用于数组类型的接口 (索引签名)
Section titled “用于数组类型的接口 (索引签名)”接口可以定义用于访问类数组结构的索引类型以及存储的值的类型。
// 描述一个键为数字、值为字符串的类数组对象interface StringArray { [index: number]: string;}
let myStrArray: StringArray;myStrArray = ["Bob", "Alice"];console.log(myStrArray[0]); // Output: Bob// myStrArray[2] = 123; // 错误:类型 'number' 不能赋值给类型 'string'。
// 描述一个键为字符串、值为数字的对象interface NumberDictionary { [index: string]: number; length: number; // 也可以有其他特定的属性 // name: string; // 错误:类型为 'string' 的属性 'name' 不能赋值给字符串索引类型 'number'。}
let ageMap: NumberDictionary = { "Alice": 30, "Bob": 35, length: 2};
console.log(ageMap["Alice"]); // Output: 30console.log(ageMap.length); // Output: 2// ageMap["Charlie"] = "forty"; // 错误:类型 'string' 不能赋值给类型 'number'。注意:如果一个接口同时具有字符串和数字索引签名,则数字索引器返回的类型必须是字符串索引器返回类型的子类型。此外,具有特定名称的属性也必须符合索引签名的类型。
扩展接口 (继承)
Section titled “扩展接口 (继承)”接口可以使用 extends 关键字继承一个或多个其他接口。这允许组合多个接口定义。
语法:单继承
Section titled “语法:单继承”interface ChildInterface extends ParentInterface { ... }语法:多重继承
Section titled “语法:多重继承”interface ChildInterface extends Parent1, Parent2, ... { ... }示例:接口继承
Section titled “示例:接口继承”interface Shape { color: string;}
interface PenStroke { penWidth: number;}
// Square extends Shape and PenStrokeinterface Square extends Shape, PenStroke { sideLength: number;}
let mySquare: Square = { color: "blue", penWidth: 5.0, sideLength: 10};
console.log(`Square Color: ${mySquare.color}`);console.log(`Square Pen Width: ${mySquare.penWidth}`);console.log(`Square Side Length: ${mySquare.sideLength}`);接口 vs. 类型别名
Section titled “接口 vs. 类型别名”TypeScript 也有类型别名 (type aliases)(使用 type 关键字),它们通常可以达到与接口类似的效果,特别是用于定义对象结构时。
type Point = { x: number; y: number;};
interface IPoint { x: number; y: number;}主要区别:
- 可扩展性: 接口可以被多次声明并会合并(声明合并)。接口也可以使用
extends进行扩展。类型别名创建后不能合并,但可以使用交叉类型 (&) 来组合类型。 - 实现: 类可以
implement(实现) 接口,以确保它们遵循契约。类不能直接implement定义对象结构的类型别名(尽管它们可以通过交叉类型等方式实现从类型别名创建的类型)。 - 灵活性: 类型别名稍具灵活性;它们不仅可以表示对象结构,还可以表示联合类型 (union types)、交叉类型 (intersection types)、元组 (tuples)、映射类型 (mapped types) 和基本类型别名(例如,
type UserID = string;)。
主要使用接口来定义对象结构或类应该实现的契约。使用类型别名来进行更通用的类型定义,包括联合类型、交叉类型、元组,或当你需要接口不提供的功能时。