Typescript

9 分钟
33 阅读
Typescript

Ts中的继承

使用接口扩展(extends)

适用场景:当原类型是接口或需要通过接口继承时
实现方式:

用 extends 关键字继承原接口
直接添加新属性的定义

ts 复制代码
interface BaseType {
  id: number;
  name: string;
}

// 继承并添加新属性
interface ExtendedType extends BaseType {
  age: number;      // 新增必选属性
  email?: string;   // 新增可选属性
}

// 使用示例
const user: ExtendedType = {
  id: 1,
  name: "Alice",
  age: 30
};

特点:
类型安全:编译时会严格检查所有必填属性
支持多继承:例如 interface A extends B, C

补充技巧
声明合并
如果是全局接口,可以通过重复声明自动合并属性:

ts 复制代码
interface Car { speed: number; }
interface Car { color: string; } // 自动合并

const myCar: Car = { speed: 120, color: "red" }; // 合法

注意:仅适用于全局或模块级作用域

覆盖属性类型
在继承时可以重写父类型属性(需保持类型兼容性)

ts 复制代码
interface Animal { legs: number; }
interface Spider extends Animal {
  legs: 8; // 覆盖为字面量类型
}

动态扩展(索引签名)
如果需要运行时动态添加属性,可在接口中定义索引签名:

ts 复制代码
interface DynamicObject {
  [key: string]: any;
  baseProp: string;
}
const obj: DynamicObject = { baseProp: "test" };
obj.newProp = 123; // 允许动态添加

代价:会牺牲部分类型安全性

使用类型别名交叉类型(&)

适用场景:当原类型是 type 或需要更灵活的类型操作时

通过 & 合并原类型和新属性

ts 复制代码
type BaseType = {
  id: number;
  name: string;
};

// 继承并扩展
type ExtendedType = BaseType & {
  age: number;
  address?: string;
};

// 使用示例
const product: ExtendedType = {
  id: 100,
  name: "Laptop",
  age: 2 // 产品上市年限
};

特点:
灵活性:可结合联合类型、条件类型等高级特性
适用于复杂类型操作:例如动态生成类型

泛型

(1)通过作用理解泛型
用来弥补any没有语法提示和报错的缺点。
最开始不指定类型,后面根据我们传入的类型确定类型。

三、泛型约束
默认情况下我们可以指定泛型为任意类型,但是有些情况下我们需要指定的类型满足某些条件后才能指定
那么这个时候我们就可以使用泛型约束。
typescript 代码解读复制代码// 需求: 要求指定的泛型类型必须有Length属性才可以

泛型(Generics)详解

泛型是 TypeScript 中非常重要的特性,它允许我们创建可重用的组件,这些组件可以支持多种类型而不是单一类型。

一、泛型基础

1. 泛型函数

typescript 复制代码
// 普通函数只能返回一种类型
function identity(arg: number): number {
  return arg;
}

// 泛型函数可以适用于多种类型
function identity<T>(arg: T): T {
  return arg;
}

// 使用方式1:显式指定类型
let output = identity<string>("myString");

// 使用方式2:类型推断
let output2 = identity("myString"); // 自动推断为string类型

2. 泛型接口

typescript 复制代码
interface GenericIdentityFn<T> {
  (arg: T): T;
}

function identity<T>(arg: T): T {
  return arg;
}

let myIdentity: GenericIdentityFn<number> = identity;

3. 泛型类

typescript 复制代码
class GenericNumber<T> {
  zeroValue: T;
  add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function(x, y) { return x + y; };

二、泛型约束

1. 基本约束

typescript 复制代码
interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length); // 现在编译器知道arg有length属性
  return arg;
}

loggingIdentity(3); // 错误,数字没有length属性
loggingIdentity({length: 10, value: 3}); // 正确

2. 在泛型约束中使用类型参数

typescript 复制代码
function getProperty<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

let x = { a: 1, b: 2, c: 3, d: 4 };

getProperty(x, "a"); // 正确
getProperty(x, "m"); // 错误: m不是x的属性

三、泛型工具类型

TypeScript 提供了一些内置的泛型工具类型:

1. Partial<T> - 将所有属性变为可选

typescript 复制代码
interface Todo {
  title: string;
  description: string;
}

function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
  return { ...todo, ...fieldsToUpdate };
}

const todo1 = {
  title: "organize desk",
  description: "clear clutter"
};

const todo2 = updateTodo(todo1, {
  description: "throw out trash"
});

2. Required<T> - 将所有属性变为必选

typescript 复制代码
interface Props {
  a?: number;
  b?: string;
}

const obj: Props = { a: 5 };

const obj2: Required<Props> = { a: 5 }; // 错误: 缺少属性b

3. Readonly<T> - 将所有属性变为只读

typescript 复制代码
interface Todo {
  title: string;
}

const todo: Readonly<Todo> = {
  title: "Delete inactive users"
};

todo.title = "Hello"; // 错误: 无法分配到"title",因为它是只读属性

4. Record<K,T> - 构造一个对象类型,其属性键为K,属性值为T

typescript 复制代码
interface CatInfo {
  age: number;
  breed: string;
}

type CatName = "miffy" | "boris" | "mordred";

const cats: Record<CatName, CatInfo> = {
  miffy: { age: 10, breed: "Persian" },
  boris: { age: 5, breed: "Maine Coon" },
  mordred: { age: 16, breed: "British Shorthair" }
};

5. Pick<T,K> - 从T中选取一组属性K

typescript 复制代码
interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Pick<Todo, "title" | "completed">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false
};

6. Omit<T,K> - 从T中排除一组属性K

typescript 复制代码
interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Omit<Todo, "description">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false
};

7. Exclude<T,U> - 从T中排除可以赋值给U的类型

typescript 复制代码
type T0 = Exclude<"a" | "b" | "c", "a">;  // "b" | "c"
type T1 = Exclude<string | number | (() => void), Function>;  // string | number

8. Extract<T,U> - 从T中提取可以赋值给U的类型

typescript 复制代码
type T0 = Extract<"a" | "b" | "c", "a" | "f">;  // "a"
type T1 = Extract<string | number | (() => void), Function>;  // () => void

9. NonNullable<T> - 从T中排除null和undefined

typescript 复制代码
type T0 = NonNullable<string | number | undefined>;  // string | number
type T1 = NonNullable<string[] | null | undefined>;  // string[]

10. ReturnType<T> - 获取函数类型的返回类型

typescript 复制代码
type T0 = ReturnType<() => string>;  // string
type T1 = ReturnType<(s: string) => void>;  // void
type T2 = ReturnType<<T>() => T>;  // unknown

11. Parameters<T> - 获取函数类型的参数类型

typescript 复制代码
declare function f1(arg: { a: number; b: string }): void;

type T0 = Parameters<() => string>;  // []
type T1 = Parameters<(s: string) => void>;  // [s: string]
type T2 = Parameters<<T>(arg: T) => T>;  // [arg: unknown]
type T3 = Parameters<typeof f1>;  // [arg: { a: number; b: string }]

四、高级泛型技巧

1. 条件类型

typescript 复制代码
type TypeName<T> =
  T extends string ? "string" :
  T extends number ? "number" :
  T extends boolean ? "boolean" :
  T extends undefined ? "undefined" :
  T extends Function ? "function" :
  "object";

type T0 = TypeName<string>;  // "string"
type T1 = TypeName<"a">;  // "string"
type T2 = TypeName<true>;  // "boolean"
type T3 = TypeName<() => void>;  // "function"
type T4 = TypeName<string[]>;  // "object"

2. 分布式条件类型

typescript 复制代码
type BoxedValue<T> = { value: T };
type BoxedArray<T> = { array: T[] };
type Boxed<T> = T extends any[] ? BoxedArray<T[number]> : BoxedValue<T>;

type T1 = Boxed<string>;  // { value: string }
type T2 = Boxed<number[]>;  // { array: number[] }
type T3 = Boxed<string | number[]>;  // BoxedValue<string> | BoxedArray<number>

3. 类型推断(infer)

typescript 复制代码
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

type Unpacked<T> =
  T extends (infer U)[] ? U :
  T extends (...args: any[]) => infer U ? U :
  T extends Promise<infer U> ? U :
  T;

type T0 = Unpacked<string>;  // string
type T1 = Unpacked<string[]>;  // string
type T2 = Unpacked<() => string>;  // string
type T3 = Unpacked<Promise<string>>;  // string
type T4 = Unpacked<Promise<string>[]>;  // Promise<string>
type T5 = Unpacked<Unpacked<Promise<string>[]>>;  // string

4. 映射类型

typescript 复制代码
type Keys = 'option1' | 'option2';
type Flags = { [K in Keys]: boolean };

// 等同于
type Flags = {
  option1: boolean;
  option2: boolean;
}

5. 键重映射(as)

typescript 复制代码
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
};

interface Person {
  name: string;
  age: number;
  location: string;
}

type LazyPerson = Getters<Person>;
// 等同于
// type LazyPerson = {
//   getName: () => string;
//   getAge: () => number;
//   getLocation: () => string;
// }

五、实际应用示例

1. 泛型React组件

typescript 复制代码
interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}

function List<T>(props: ListProps<T>) {
  return (
    <div>
      {props.items.map(props.renderItem)}
    </div>
  );
}

// 使用
<List<number>
  items={[1, 2, 3]}
  renderItem={(item) => <div key={item}>{item}</div>}
/>

2. API响应类型

typescript 复制代码
interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

async function fetchUser(): Promise<ApiResponse<User>> {
  const response = await fetch('/api/user');
  return response.json();
}

3. 高阶函数

typescript 复制代码
function debounce<F extends (...args: any[]) => any>(
  func: F,
  delay: number
): (...args: Parameters<F>) => void {
  let timeoutId: ReturnType<typeof setTimeout>;
  
  return function(this: any, ...args: Parameters<F>) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func.apply(this, args), delay);
  };
}

// 使用
const debouncedLog = debounce(console.log, 300);
debouncedLog("Hello, world!");

总结

TypeScript 泛型提供了强大的类型抽象能力,可以:

  1. 创建可重用的组件,支持多种类型
  2. 保持类型安全,避免使用 any 类型
  3. 利用类型推断减少冗余代码
  4. 通过约束控制类型参数的范围
  5. 使用工具类型简化常见类型转换

掌握泛型可以显著提高 TypeScript 代码的质量和可维护性,特别是在构建大型应用程序或库时。

评论

评论

发表评论