范型
TS(TypeScript)中的泛型是一种参数化类型的机制,允许在定义函数、类或接口时使用类型变量来表示参数或返回值的类型。通过使用泛型,可以将类型的具体实现留给使用者来决定,从而增加代码的灵活性和复用性。
在 TS 中,可以使用尖括号 <>
将泛型参数放置在函数名或类名后面,例如:
function identity<T>(arg: T): T {
return arg;
}
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
在上面的例子中,identity
函数使用了一个类型变量 T
来表示传入参数和返回值的类型,并将该类型变量放在函数名后面的尖括号内。
GenericNumber
类也使用了类型变量 T
来表示其属性 zeroValue
和方法 add
的类型,从而使得该类可以适用于任意类型的数值。
在调用泛型函数或实例化泛型类时,需要在尖括号内指定类型参数的具体类型,例如:
let output = identity<string>("Hello World");
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
上述代码中,通过在 identity
函数和 GenericNumber
类名后的尖括号内指定具体的类型参数,分别得到了一个返回字符串类型的 output
变量和一个处理数字类型的 myGenericNumber
实例。
泛型在接口和类中的使用
在接口中,可以使用泛型来定义一个通用的类型,使其适用于多种类型。例如:
interface MyInterface<T> {
property: T;
method(): T;
}
上述代码中,MyInterface
接口使用了泛型 T
来定义 property
和 method()
的返回值类型。这样,当我们实现 MyInterface
接口时,可以指定 T
的具体类型,并保证 property
和 method()
的返回值类型与 T
相同。
在类中,泛型可以用于定义类的属性、方法和构造函数参数类型。例如:
class MyClass<T> {
private prop: T;
constructor(arg: T) {
this.prop = arg;
}
public getProp(): T {
return this.prop;
}
}
上述代码中,MyClass
类使用了泛型 T
来定义 prop
属性的类型以及构造函数的参数类型。这样,我们可以创建一个 MyClass
实例并传入任何类型的参数,而 prop
属性的类型和 getProp()
方法的返回值类型都将与传入的参数类型相同。
模块化
模块的基本概念
在TypeScript中,模块是指包含代码的独立单元,可以将其导出以供其他模块使用,也可以从其他模块中导入代码。
TypeScript中的模块采用了标准的ES6模块语法,并增加了一些额外的功能,例如命名空间、模块别名等。
在一个TypeScript应用中,每个文件都被视为一个模块,其中定义的变量、函数、类等默认情况下是私有的,如果需要在其他模块中使用,需要使用export关键字将其导出。同时,使用import关键字可以从其他模块中导入代码。
模块导出和引入
模块可以包含变量、函数、类和类型声明等内容,并且可以将它们导出供其他模块使用。
下面是一些常见的模块导出语法:
- 导出变量或常量:
export const myVariable = 123;
- 导出函数:
export function myFunction() {
// ...
}
- 导出类:
export class MyClass {
// ...
}
- 导出类型声明:
export interface MyInterface {
// ...
}
模块中的导入语法如下:
- 导入默认导出:
import myModule from './myModule';
- 导入具名导出:
import { myVariable, myFunction, MyClass } from './myModule';
- 导入所有导出:
import * as myModule from './myModule';
需要注意的是,在 ES6 模块系统中,导入和导出都是静态的,这意味着它们只能出现在模块的顶层作用域中,并且不能在函数或语句块中使用。此外,ES6 模块系统也支持循环依赖和动态导入等高级特性,可以更加灵活地组织和加载模块。
默认导出和命名导出
- 默认导出
默认导出是指一个模块只能够输出一个值,这个值可以是任意类型的。在一个模块中,通过使用export default关键字来进行默认导出。例如:
// module.ts
export default function add(a: number, b: number): number {
return a + b;
}
// app.ts
import add from './module';
console.log(add(1, 2)); // 输出 3
在app.ts文件中,我们使用import语句引入了module.ts文件中的默认导出项add函数。注意,在import语句中,我们没有使用花括号{}包含add,这是因为默认导出不需要使用变量名来标识。
- 命名导出
命名导出是指一个模块可以输出多个值,每个值都有一个名称。在一个模块中,通过使用export关键字加上标识符来进行命名导出。例如:
// module.ts
export function add(a: number, b: number): number {
return a + b;
}
export const PI = 3.14;
// app.ts
import { add, PI } from './module';
console.log(add(1, 2)); // 输出 3
console.log(PI); // 输出 3.14
在app.ts文件中,我们使用import语句引入了module.ts文件中的命名导出项add函数和常量PI。注意,在import语句中,我们使用了花括号{}来包含add和PI,这是因为命名导出需要使用变量名来标识。
命名空间
命名空间通过使用关键字 namespace
来定义,可以在一个命名空间内声明变量、函数、类等。例如:
namespace MyNamespace {
export const message = "Hello World";
export function sayHello() {
console.log(message);
}
export class Person {
constructor(public name: string) {}
}
}
在上面的例子中,我们创建了一个名为 MyNamespace
的命名空间,并在其中声明了一个常量、一个函数和一个类。注意到在命名空间外部无法访问这些成员,因为它们被封装在了命名空间内。
要在命名空间外部使用命名空间中的成员,需要使用 export
关键字进行导出。例如,在下面的代码中,我们可以使用 MyNamespace.message
和 MyNamespace.Person
:
import { MyNamespace } from "./my-namespace";
console.log(MyNamespace.message); // 输出 "Hello World"
const person = new MyNamespace.Person("Alice");
console.log(person.name); // 输出 "Alice"