泛型与继承
泛型的基本概念
泛型是TypeScript中用于创建可复用组件的核心工具。它允许在定义函数、接口或类时不预先指定具体类型,而是在使用时再指定。泛型的核心思想是参数化类型,即把类型当作参数传递。
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>("hello");
let output2 = identity<number>(42);
泛型通过<T>
语法声明类型参数,这个T可以在函数体中使用。调用时可以显式指定类型,也可以让TypeScript自动推断类型。泛型的主要优势在于保持类型安全的同时提供了代码复用性。
继承的基本概念
继承是面向对象编程的重要特性,TypeScript通过extends
关键字实现类继承。子类可以继承父类的属性和方法,同时可以添加自己的特性或覆盖父类行为。
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number = 0) {
console.log(`${this.name} moved ${distance}m`);
}
}
class Dog extends Animal {
bark() {
console.log("Woof! Woof!");
}
}
const dog = new Dog("Buddy");
dog.bark(); // Woof! Woof!
dog.move(10); // Buddy moved 10m
泛型与继承的结合
当泛型遇到继承时,可以创建出更灵活的类型系统。泛型类可以继承自非泛型类,非泛型类也可以继承自泛型类,甚至泛型类之间也可以相互继承。
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
class StringNumber extends GenericNumber<string> {
constructor() {
super();
this.zeroValue = "";
this.add = (x, y) => x + y;
}
}
const stringNum = new StringNumber();
console.log(stringNum.add("Hello", "World")); // HelloWorld
泛型约束与继承
通过extends
关键字可以对泛型参数进行约束,限制它必须符合某种类型。这在需要访问特定属性或方法时特别有用。
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
loggingIdentity("hello"); // 5
loggingIdentity([1, 2, 3]); // 3
loggingIdentity({length: 10, value: "test"}); // 10
泛型类继承中的类型参数
泛型类继承时,子类可以保留父类的类型参数,也可以固定部分或全部类型参数。
class Base<T, U> {
constructor(public prop1: T, public prop2: U) {}
}
class Derived1<V> extends Base<string, V> {
constructor(prop2: V) {
super("default", prop2);
}
}
class Derived2 extends Base<number, boolean> {
constructor(prop1: number, prop2: boolean) {
super(prop1, prop2);
}
}
方法重写与泛型
子类可以重写父类的泛型方法,但必须保持兼容的签名。返回类型可以是父类方法返回类型的子类型。
class Parent {
process<T>(input: T): T[] {
return [input];
}
}
class Child extends Parent {
process<T extends string>(input: T): string[] {
return [input.toUpperCase()];
}
}
const child = new Child();
console.log(child.process("hello")); // ["HELLO"]
泛型接口继承
接口也可以使用泛型,并且支持继承。泛型接口可以继承非泛型接口,反之亦然。
interface NotGeneric {
id: number;
}
interface Generic<T> extends NotGeneric {
value: T;
}
interface Pair<T, U> {
first: T;
second: U;
}
interface NumberPair extends Pair<number, number> {
sum(): number;
}
const pair: NumberPair = {
first: 1,
second: 2,
sum() { return this.first + this.second; }
};
条件类型与继承
TypeScript 2.8引入的条件类型,结合extends
关键字,可以根据类型关系选择不同的类型。
type IsString<T> = T extends string ? "yes" : "no";
type A = IsString<string>; // "yes"
type B = IsString<number>; // "no"
type ExtractString<T> = T extends string ? T : never;
type C = ExtractString<"hello" | 42 | true>; // "hello"
泛型与类静态成员
静态成员不能直接使用类的类型参数,但可以通过泛型函数或静态泛型方法实现类似功能。
class GenericClass<T> {
static defaultValue: any; // 不能是T
static create<U>(value: U): GenericClass<U> {
const instance = new GenericClass<U>();
// 初始化逻辑
return instance;
}
}
const instance = GenericClass.create<string>("test");
高级模式:混入与泛型
混入模式结合泛型可以创建高度可复用的组件。通过泛型约束确保混入类具有所需的结构。
type Constructor<T = {}> = new (...args: any[]) => T;
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
timestamp = Date.now();
};
}
class User {
name: string;
constructor(name: string) {
this.name = name;
}
}
const TimestampedUser = Timestamped(User);
const user = new TimestampedUser("Alice");
console.log(user.timestamp); // 当前时间戳
类型推断与泛型继承
TypeScript在泛型继承场景下的类型推断能力非常强大,能够自动推导出复杂的类型关系。
class Box<T> {
value: T;
constructor(value: T) {
this.value = value;
}
map<U>(f: (x: T) => U): Box<U> {
return new Box(f(this.value));
}
}
class ExtendedBox<T> extends Box<T> {
log(): void {
console.log(this.value);
}
}
const box = new ExtendedBox(42);
box.log(); // 42
const stringBox = box.map(x => x.toString());
stringBox.log(); // "42"
泛型参数默认值
类似于函数参数默认值,泛型类型参数也可以指定默认类型。
interface PaginatedResponse<T = any> {
data: T[];
total: number;
page: number;
}
const userResponse: PaginatedResponse<{name: string}> = {
data: [{name: "Alice"}],
total: 1,
page: 1
};
const anyResponse: PaginatedResponse = {
data: [1, 2, 3],
total: 3,
page: 1
};
泛型与装饰器
装饰器可以应用于泛型类,但需要注意类型信息的处理方式。
function logClass<T extends {new(...args: any[]): {}}>(constructor: T) {
return class extends constructor {
constructor(...args: any[]) {
super(...args);
console.log(`Instance created: ${constructor.name}`);
}
};
}
@logClass
class GenericEntity<T> {
constructor(public value: T) {}
}
const entity = new GenericEntity<string>("test"); // 输出: Instance created: GenericEntity
泛型与索引类型
结合索引类型和泛型可以创建灵活的类型操作工具。
function pluck<T, K extends keyof T>(objs: T[], key: K): T[K][] {
return objs.map(obj => obj[key]);
}
const people = [
{name: "Alice", age: 30},
{name: "Bob", age: 25}
];
const names = pluck(people, "name"); // string[]
const ages = pluck(people, "age"); // number[]
泛型与递归类型
泛型可以用于定义递归类型结构,这在处理树形数据时特别有用。
type TreeNode<T> = {
value: T;
left?: TreeNode<T>;
right?: TreeNode<T>;
};
const numberTree: TreeNode<number> = {
value: 1,
left: {
value: 2,
left: {value: 4}
},
right: {
value: 3
}
};
function traverse<T>(node: TreeNode<T>, visit: (value: T) => void) {
visit(node.value);
if (node.left) traverse(node.left, visit);
if (node.right) traverse(node.right, visit);
}
traverse(numberTree, console.log); // 依次输出 1, 2, 4, 3
泛型与Promise
Promise天然就是泛型的,可以表示异步操作的最终结果类型。
function fetchData<T>(url: string): Promise<T> {
return fetch(url).then(response => response.json());
}
interface User {
id: number;
name: string;
}
fetchData<User[]>("/api/users")
.then(users => {
users.forEach(user => console.log(user.name));
});
泛型与React组件
在React中使用TypeScript时,泛型可以用于创建可复用的组件。
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({items, renderItem}: ListProps<T>) {
return (
<ul>
{items.map((item, index) => (
<li key={index}>{renderItem(item)}</li>
))}
</ul>
);
}
const users = [{id: 1, name: "Alice"}, {id: 2, name: "Bob"}];
<UserList users={users} />;
function UserList({users}: {users: Array<{id: number, name: string}>}) {
return (
<List
items={users}
renderItem={user => <span>{user.name}</span>}
/>
);
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:泛型与函数重载
下一篇:弱网环境下的体验优化