joken-前端工程师

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: :: :: 管理 ::
  394 随笔 :: 39 文章 :: 8 评论 :: 20万 阅读

常用语法

1. 基本类型注解

TypeScript 的核心是类型系统,基本类型注解是最常见的使用方式。

// 基本类型
let name: string = "Alice";
let age: number = 25;
let isActive: boolean = true;

// 数组
let numbers: number[] = [1, 2, 3];
// 或者使用泛型形式
let strings: Array<string> = ["a", "b", "c"];

// 对象
let person: { name: string; age: number } = { name: "Bob", age: 30 };

实用场景:明确变量类型,避免类型错误,尤其在团队协作中。


2. 接口 (Interface)

接口是定义对象结构的常用方式,可复用且支持扩展。

interface User {
  name: string;
  age: number;
  email?: string; // 可选属性
}

// 使用接口
const user: User = { name: "Charlie", age: 28 };

// 接口扩展
interface Admin extends User {
  role: string;
}

const admin: Admin = { name: "Dave", age: 35, role: "admin" };

实用场景:定义 API 返回数据结构、组件 props 类型等。


3. 泛型 (Generics)

泛型让函数或类在类型上更灵活,同时保持类型安全。

// 泛型函数
function getFirst<T>(array: T[]): T | undefined {
  return array[0];
}

const firstNum = getFirst<number>([1, 2, 3]); // 1
const firstStr = getFirst<string>(["a", "b", "c"]); // "a"

// 泛型接口
interface Response<T> {
  data: T;
  status: number;
}

const userResponse: Response<{ name: string }> = {
  data: { name: "Eve" },
  status: 200,
};

实用场景:编写可重用的工具函数、处理不同类型的 API 响应。


4. 联合类型 (Union Types) 和类型别名 (Type Alias)

联合类型和类型别名让类型更灵活。

// 联合类型
type ID = string | number;
let userId: ID = "abc123";
userId = 123; // 也合法

// 类型别名
type Status = "success" | "error" | "loading";
let currentStatus: Status = "loading";

实用场景:处理多种可能的值(如状态管理)或简化复杂类型。


5. 工具类型 (Utility Types)

TypeScript 内置了一些实用工具类型,简化类型操作。

// Partial:将所有属性变为可选
interface Todo {
  title: string;
  completed: boolean;
}
const partialTodo: Partial<Todo> = { title: "Learn TS" };

// Pick:挑选部分属性
type TodoPreview = Pick<Todo, "title">;
const preview: TodoPreview = { title: "Learn TS" };

// Omit:排除部分属性
type TodoWithoutCompleted = Omit<Todo, "completed">;
const todo: TodoWithoutCompleted = { title: "Learn TS" };

// ReturnType:获取函数返回类型
function getUser() {
  return { name: "Frank", age: 40 };
}
type UserType = ReturnType<typeof getUser>; // { name: string; age: number }

实用场景:操作已有类型,减少重复定义。


6. 类型断言 (Type Assertion)

当你比 TypeScript 更清楚变量类型时,可以使用类型断言。

let someValue: any = "this is a string";
// 使用 as 断言
let strLength: number = (someValue as string).length;

// 非空断言 (!)
let element: HTMLElement | null = document.querySelector("#app");
let app = element!.innerText; // 确定 element 不为 null

实用场景:处理不确定类型的数据(如 DOM 操作或第三方库)。


7. 枚举 (Enums)

枚举用于定义一组命名常量,增强代码可读性。

enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}

let move: Direction = Direction.Up; // "UP"

实用场景:定义有限的选项(如状态码、方向)。


8. 类型守卫 (Type Guards)

通过条件判断收窄类型范围。

function isString(value: any): value is string {
  return typeof value === "string";
}

function process(value: string | number) {
  if (isString(value)) {
    console.log(value.toUpperCase()); // value 被收窄为 string
  } else {
    console.log(value.toFixed(2)); // value 被收窄为 number
  }
}

实用场景:处理联合类型时确保类型安全。


9. 模块化和类型声明

为第三方库或纯 JS 文件提供类型支持。

// declare 声明全局变量
declare const myGlobalVar: string;

// 为无类型的模块添加类型
declare module "some-npm-package" {
  export function doSomething(): string;
}

实用场景:集成无类型支持的库。


10. 条件类型 (Conditional Types)

条件类型让类型更动态。

type IsString<T> = T extends string ? "yes" : "no";

type Test1 = IsString<string>; // "yes"
type Test2 = IsString<number>; // "no"

实用场景:高级类型推导,编写复杂类型逻辑。


总结

这些用法涵盖了 TypeScript 的核心特性:

  • 类型安全:基本类型、接口、类型守卫。
  • 灵活性:泛型、工具类型、条件类型。
  • 可维护性:枚举、类型别名、模块化。

函数类型声明

1. 直接声明函数的类型

为函数变量或参数直接指定类型,使用 (参数: 类型) => 返回值类型 的语法。

// 函数类型声明
let add: (a: number, b: number) => number;

// 赋值实现
add = (a, b) => a + b;

console.log(add(2, 3)); // 输出: 5

要点

  • 参数类型和返回类型用箭头 => 分隔。
  • 如果函数没有返回值,可以用 void
let log: (message: string) => void;
log = (message) => console.log(message);

2. 在接口或类型别名中声明函数类型

使用 interfacetype 定义函数类型,常用于描述对象属性或复杂结构。

interface

interface MathOperation {
  (x: number, y: number): number;
}

const multiply: MathOperation = (x, y) => x * y;
console.log(multiply(4, 5)); // 输出: 20

type

type Greet = (name: string) => string;

const sayHello: Greet = (name) => `Hello, ${name}`;
console.log(sayHello("Alice")); // 输出: "Hello, Alice"

区别

  • interface 更适合定义对象结构,可扩展。
  • type 更灵活,适合简单类型或联合类型。

3. 函数参数和返回值类型的推断

如果函数实现时直接指定了参数和返回值的类型,TypeScript 可以自动推断类型,无需显式声明函数类型。

const subtract = (a: number, b: number): number => a - b;
// 类型自动推断为 (a: number, b: number) => number
console.log(subtract(10, 7)); // 输出: 3

但如果函数类型需要复用或传递,建议显式声明。


4. 可选参数和默认参数

函数参数可以是可选的(用 ?)或带默认值。

// 可选参数
type PrintInfo = (name: string, age?: number) => void;
const printInfo: PrintInfo = (name, age) => {
  console.log(`Name: ${name}${age ? `, Age: ${age}` : ""}`);
};
printInfo("Bob"); // 输出: "Name: Bob"
printInfo("Bob", 30); // 输出: "Name: Bob, Age: 30"

// 默认参数
type GreetWithDefault = (name: string, greeting?: string) => string;
const greet: GreetWithDefault = (name, greeting = "Hi") => `${greeting}, ${name}`;
console.log(greet("Charlie")); // 输出: "Hi, Charlie"

注意:可选参数必须放在必选参数后面。


5. 剩余参数 (Rest Parameters)

使用 ... 表示剩余参数,类型为数组。

type Sum = (...numbers: number[]) => number;
const sum: Sum = (...numbers) => numbers.reduce((acc, curr) => acc + curr, 0);
console.log(sum(1, 2, 3, 4)); // 输出: 10

6. 函数重载 (Function Overloads)

当函数根据参数类型返回不同类型时,可以使用函数重载。

function convert(input: string): number;
function convert(input: number): string;
function convert(input: string | number): string | number {
  if (typeof input === "string") return parseInt(input);
  return input.toString();
}

const num = convert("123"); // 类型: number
const str = convert(456); // 类型: string

要点

  • 重载签名写在实现签名之前。
  • 实现签名需兼容所有重载。

7. 回调函数类型

在函数参数中声明回调函数的类型。

function fetchData(callback: (data: string) => void): void {
  const data = "Some data";
  callback(data);
}

fetchData((data) => console.log(data)); // 输出: "Some data"

实用场景:异步操作、事件处理。


8. 泛型函数类型

为函数添加泛型,增强灵活性。

type IdentityFn<T> = (value: T) => T;
const identity: IdentityFn<number> = (value) => value;
console.log(identity(42)); // 输出: 42

// 直接在函数定义中使用泛型
function map<T, U>(array: T[], transform: (item: T) => U): U[] {
  return array.map(transform);
}
const lengths = map(["a", "bc"], (s) => s.length); // 类型: number[]

实用场景:处理动态类型,如数组操作。


9. 异步函数类型

异步函数返回 Promise,需指定返回值的类型。

type AsyncFn = (id: number) => Promise<string>;
const getData: AsyncFn = async (id) => {
  return `Data for id ${id}`;
};

getData(1).then(console.log); // 输出: "Data for id 1"

总结

以下是函数类型声明的常用方式及其适用场景:

  • 直接类型声明:简单函数变量。
  • 接口/类型别名:复用函数签名。
  • 泛型:动态类型处理。
  • 重载:多类型返回。
  • 异步:Promise 返回值。

联合类型、交叉类型

1. 联合类型 (Union Types)

定义

联合类型表示一个值可以是多种类型中的一种,用 | 分隔不同类型。

语法

type UnionType = Type1 | Type2 | Type3;

示例

type Status = "success" | "error" | "loading";
let currentStatus: Status = "success"; // 合法
currentStatus = "error"; // 合法
// currentStatus = "pending"; // 错误: 类型不匹配

// 联合类型与基本类型
type ID = string | number;
let userId: ID = "abc123";
userId = 123; // 都合法

特点

  • 类型收窄:使用类型守卫(如 typeofin、自定义守卫)区分具体类型。
  • 只能访问公共成员:当使用联合类型时,只能访问所有类型共有的属性或方法。
function printId(id: string | number) {
  if (typeof id === "string") {
    console.log(id.toUpperCase()); // id 被收窄为 string
  } else {
    console.log(id.toFixed(2)); // id 被收窄为 number
  }
}
printId("hello"); // 输出: "HELLO"
printId(123.456); // 输出: "123.46"

实用场景

  • 表示变量可能有多种类型(如函数参数、API 返回值)。
  • 定义有限的选项(如状态枚举)。

2. 交叉类型 (Intersection Types)

定义

交叉类型表示一个值同时具有多种类型的特性,用 & 组合多个类型。

语法

type IntersectionType = Type1 & Type2 & Type3;

示例

interface Person {
  name: string;
}
interface Employee {
  id: number;
}

type Staff = Person & Employee;

const staff: Staff = {
  name: "Alice",
  id: 1001,
}; // 必须同时满足 Person 和 Employee

特点

  • 合并类型:交叉类型将多个类型的成员合并为一个新类型。
  • 属性要求:值必须具备所有类型的属性。
  • 与对象类型更相关:交叉类型通常用于对象类型,基本类型交叉可能导致 never
type A = { a: string };
type B = { b: number };
type AB = A & B;

const obj: AB = { a: "hello", b: 42 }; // 必须同时有 a 和 b

基本类型交叉

当交叉类型应用于不相容的基本类型时,结果可能是 never(无任何值能满足)。

type StrAndNum = string & number; // 类型为 never,因为 string 和 number 无交集
let value: StrAndNum; // 无法赋值任何值

实用场景

  • 合并多个接口(如描述一个对象同时具有多种角色)。
  • 在类型系统中扩展已有类型。

3. 联合类型 vs 交叉类型

| 特性 | 联合类型 (Union) | | 交叉类型 (Intersection) & |
|------------------|------------------------------|------------------------------|
| 含义 | “或”关系,可以是任一类型 | “与”关系,必须同时满足所有类型 |
| 语法 | A \| B | A & B |
| 结果 | 值是 A 或 B 中的一种 | 值同时具有 A 和 B 的特性 |
| 典型使用 | 表示多种可能 | 合并类型特性 |
| 访问限制 | 只能访问公共成员 | 可访问所有成员 |
| 示例 | string \| number | {a: string} & {b: number} |

对比示例

// 联合类型
type Union = { a: string } | { b: number };
const unionValue: Union = { a: "foo" }; // 合法
const unionValue2: Union = { b: 123 }; // 合法
// const unionValue3: Union = { a: "foo", b: 123 }; // 可行,但不是必须

// 交叉类型
type Intersection = { a: string } & { b: number };
const intersectionValue: Intersection = { a: "foo", b: 123 }; // 必须同时有 a 和 b
// const intersectionValue2: Intersection = { a: "foo" }; // 错误,缺少 b

4. 高级用法

联合类型与类型守卫

function process(value: string | number | boolean) {
  if (typeof value === "string") {
    return value.toUpperCase();
  } else if (typeof value === "number") {
    return value * 2;
  } else {
    return !value;
  }
}

交叉类型与泛型

type Merge<T, U> = T & U;

interface Cat {
  meow(): void;
}
interface Dog {
  bark(): void;
}

type CatDog = Merge<Cat, Dog>;
const pet: CatDog = {
  meow: () => console.log("Meow"),
  bark: () => console.log("Woof"),
};
pet.meow(); // "Meow"
pet.bark(); // "Woof"

条件类型中的联合与交叉

type Check<T> = T extends string ? "string" : "other";
type Result = Check<string | number>; // "string" | "other"

type Combine<T, U> = T & U;
type Combined = Combine<{ a: string }, { b: number }>; // { a: string; b: number }

5. 注意事项

  • 联合类型的歧义:如果联合类型成员没有公共方法,访问时需类型收窄。
  • 交叉类型的冲突:如果交叉的类型有同名属性但类型不同,结果可能是 never
type Conflict = { x: string } & { x: number }; // x 的类型为 never

总结

  • 联合类型:适用于“或”的场景,表示多种可能之一。
  • 交叉类型:适用于“与”的场景,合并多种特性。

extends 主要使用场景

1. 接口继承 (Interface Extension)

定义

extends 用于让一个接口继承另一个接口的属性,扩展已有类型定义。

示例

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

interface Employee extends Person {
  employeeId: string;
}

const employee: Employee = {
  name: "Alice",
  age: 30,
  employeeId: "E123",
};

使用场景

  • 扩展对象类型:当需要在已有接口基础上添加新属性时。
  • 代码复用:避免重复定义公共属性。
  • 实用例子:定义 React 组件的 props 类型时,扩展基础 props。
interface BaseProps {
  className: string;
}

interface ButtonProps extends BaseProps {
  onClick: () => void;
}

2. 类继承 (Class Inheritance)

定义

extends 用于类继承,让子类继承父类的属性和方法。

示例

class Animal {
  move() {
    console.log("Moving...");
  }
}

class Dog extends Animal {
  bark() {
    console.log("Woof!");
  }
}

const dog = new Dog();
dog.move(); // "Moving..."
dog.bark(); // "Woof!"

使用场景

  • 面向对象编程:实现类的层次结构。
  • 复用逻辑:父类的公共方法或属性可以被子类直接使用。
  • 实用例子:定义不同类型的控制器类,继承基础功能。

3. 泛型约束 (Generic Constraint)

定义

extends 用于约束泛型参数的类型,要求泛型类型必须符合某个条件(如具有特定属性)。

示例

interface HasLength {
  length: number;
}

function getLength<T extends HasLength>(item: T): number {
  return item.length;
}

console.log(getLength("hello")); // 5
console.log(getLength([1, 2, 3])); // 3
// getLength(42); // 错误: number 没有 length 属性

使用场景

  • 限制类型范围:确保泛型参数具有特定属性或方法。
  • 类型安全:避免传入不符合预期的类型。
  • 实用例子:处理特定结构的数组或对象。
interface Printable {
  print(): void;
}

function logItem<T extends Printable>(item: T) {
  item.print();
}

4. 条件类型 (Conditional Types)

定义

extends 在条件类型中用于类型推断和分发,语法为 T extends U ? X : Y,表示如果 TU 的子集,则结果为 X,否则为 Y

示例

type IsString<T> = T extends string ? "yes" : "no";

type Test1 = IsString<string>; // "yes"
type Test2 = IsString<number>; // "no"

分发行为(联合类型)

T 是联合类型时,extends 会对每个成员分别应用条件。

type Check<T> = T extends string ? "string" : "other";
type Result = Check<string | number>; // "string" | "other"

使用场景

  • 类型推断:根据条件动态生成类型。
  • 类型过滤:从联合类型中提取特定类型。
  • 实用例子:工具类型(如 ExtractExclude)的实现。
type ExtractString<T> = T extends string ? T : never;
type StringsOnly = ExtractString<string | number | boolean>; // string

5. 类型兼容性检查

定义

extends 可以用来检查一个类型是否兼容另一个类型,尤其在高级类型操作中。

示例

type Animal = { name: string };
type Dog = { name: string; breed: string };

type IsDogAnimal = Dog extends Animal ? "yes" : "no"; // "yes"
// 因为 Dog 的结构包含 Animal 的所有属性

使用场景

  • 类型关系验证:判断类型之间的子集关系。
  • 高级工具类型:如 keyofinfer 结合使用。

6. ** keyof 和 extends 结合**

定义

extends 常与 keyof 一起使用,约束键的范围。

示例

interface User {
  name: string;
  age: number;
}

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

const user: User = { name: "Bob", age: 25 };
console.log(getProperty(user, "name")); // "Bob"
// getProperty(user, "email"); // 错误: "email" 不是 User 的键

使用场景

  • 安全访问对象属性:确保键名有效。
  • 实用例子:工具函数中动态获取属性。

7. extends 在映射类型中

定义

在映射类型中,extends 约束键的来源。

示例

type Optional<T> = {
  [K in keyof T]?: T[K];
};

interface Config {
  host: string;
  port: number;
}

type OptionalConfig = Optional<Config>;
// { host?: string; port?: number }

使用场景

  • 类型转换:生成新类型(如所有属性可选、可为空)。
  • 实用例子:处理配置对象。

总结

extends 的主要使用场景包括:

场景 作用 示例类型/关键字
接口继承 扩展接口属性 interface
类继承 实现类的继承 class
泛型约束 限制泛型类型范围 <T extends U>
条件类型 动态决定类型 T extends U ? X : Y
类型兼容性 检查类型子集关系 条件类型
keyof 约束 限制键的范围 K extends keyof T
映射类型 控制映射键的来源 [K in keyof T]

实用建议

  • 简单场景:用接口或类继承扩展类型。
  • 复杂场景:结合泛型和条件类型实现动态逻辑。
  • 类型安全:用约束和 keyof 防止错误。
posted on   joken1310  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示