Typescript

Typescript

Typescript 类型

基础类型

  • number
  • string
  • boolean
  • symbol
  • bigint
  • null
  • undefined
  • object
  • array

复合类型

数组

Array<number>number[]

元组

[number, string]

const tuple: [number, string] = [1, "a"];
console.log(tuple[0]); // 1
console.log(tuple[1]); // a

class Person {}

特殊类型

any
任何类型都可以赋值给 any 类型.

unknown
unknown 类型是 any 类型的安全版本, 只能赋值给 unknown 类型或者 any 类型.

never
never 类型是其他类型的子类型, 表示一个永远不存在的值的类型.

void
void 类型是其他类型的子类型, 表示没有任何类型.

interface

// 定义对象类型
interface Person {
  name: string;
  age: number;
}

// 定义函数类型
interface Add {
  (a: number, b: number): number;
}

// 构造器
interface PersonConstructor {
  new (name: string, age: number): Person;
}

interface 的合并

当定义两个或多个同名的 interface 时, 它们会被合并为一个 interface, 它们的属性会被合并.

interface Person {
  name: string;
}
interface Person {
  age: number;
}
const person: Person = {
  name: "John",
  age: 30,
};

interface 和 type 的区别

interface 是一种类型的定义, 而 type 只是类型的别名, 这是它们的本质区别.
interface 是专门用来定义对象结构的, type 可以用来给任何类型起名字,包括对象、基本类型、联合类型、交叉类型等。

interface Person {
  name: string;
}
type PersonType = {
  name: string;
};
type status = "success" | "error";
type arr = number[];
特性interfacetype
描述对象
扩展(继承)✅(extends)✅(&)
声明合并
支持泛型
联合类型
元组、基本类型
被类 implements
interface 可选属性与修饰符

interface 使用 ? 定义可选属性, 使用 readonly 表示该属性或方法只读.

interface People {
  name: string;
  age: number;
  job?: string;
  readonly gender: string;
}

const people: People = {
  name: "name",
  age: 18,
  gender: "male",
};

people.gender = "female"; // 报错, 只读属性不能被修改

枚举

枚举不仅仅是类型, 而且是常量. 其他类型在编译后会被删除, 但是枚举是运行时真正存在的对象.

enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}
枚举值的自动计算

未初始化枚举值

若所有枚举值都未初始化, 则枚举值会从 0 开始自动计算. 第一个枚举值为 0, 往后递增

enum Direction {
  Up,
  Down,
  Left,
  Right,
}
console.log([Direction.Up, Direction.Down, Direction.Left, Direction.Right]); // [0, 1, 2, 3]

第一个枚举值初始化

若第一个枚举值为数值, 则枚举值会从第一个枚举值开始自动计算. 往后递增

enum Direction {
  Up = 1,
  Down,
  Left,
  Right,
}
console.log([Direction.Up, Direction.Down, Direction.Left, Direction.Right]); // [1, 2, 3, 4]

其他枚举值初始化

若第一个枚举值未初始化, 但有其他枚举值初始化为数值, 则为初始化前面的枚举值将从第一个为 0 开始自动计算, 往后递增.
初始化后面的枚举值将从初始化的枚举值开始自动计算, 往后递增.

enum Direction {
  Up,
  Down,
  Left = 3,
  Right,
}
console.log([Direction.Up, Direction.Down, Direction.Left, Direction.Right]); // [0, 1, 3, 4]

初始值为非数值

若枚举的初始值为非数值, 那么 ts 无法计算初始值时, 会要求其他枚举值必须初始化. 否则会报错.
若部分初始值为非数值, 但是有初始值为数值时, 为数值的枚举值后面的枚举值可以被自动计算.

// 报错: 枚举成员必须具有初始化表达式。
enum Direction {
  Up = "UP",
  Down,
  Left,
  Right,
}

// 可以被自动计算
enum Direction {
  Up = "UP",
  Down = 2,
  Left,
  Right,
}
console.log([Direction.Up, Direction.Down, Direction.Left, Direction.Right]); // ["UP", 2, 3, 4]
常量枚举

常量枚举是使用 const 关键字定义的枚举, 常量枚举只能作为类型使用.
常量枚举会在编译阶段被删除, 并且不会在运行时创建对象. 常量枚举成员在使用时会被内联到使用处.

注意: 常量枚举不能有计算成员

// 普通枚举
enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}
console.log(Direction); // {Up: "UP", Down: "DOWN", Left: "LEFT", Right: "RIGHT"}

// 常量枚举
const enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}
console.log(Direction); // 报错, 不能作为值在运行阶段使用

// 不能使用计算成员
const enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "right".toUpperCase(), // 报错, 常量枚举不能有计算成员,
}

函数

函数的定义
// 书写函数时定义
const add = (a: number, b: number): number => {
  return a + b;
};

// 完整定义函数类型
type AddFunction = (num1: number, num2: number) => number;
const add: AddFunction = (num1, num2) => {
  return num1 + num2;
};

// 接口定义函数类型
interface AddFunction {
  (num1: number, num2: number): number;
}
const add: AddFunction = (num1, num2) => {
  return num1 + num2;
};

// 可选参数, 可选参数必须放在必须参数后面
type AddFunction = (num1: number, num2: number) => number;

// 定义剩余参数
type AddFunction = (num1: number, ...numbers: number[]) => number;
const add: AddFunction = (num1, ...numbers) => {
  return num1 + numbers.reduce((a, b) => a + b, 0);
};

// 定义 this
interface People {
  name: string;
  age: number;
  say: (this: People) => string;
}
const people: People = {
  name: "张三",
  age: 18,
  say() {
    return `My name is ${this.name}, I am ${this.age} years old.`;
  },
};
people.say(); // "My name is 张三, I am 18 years old."
const say = people.say;
say(); // 报错, this 指向错误
函数的重载

函数重载是指对同一个函数的多次定义, 每次定义的参数类型和返回类型可能不同.
函数重载的目的是为了提高代码的可读性和可维护性.

function parseDate(date: Date): string; // 传入 Date 类型, 返回格式好的时间字符串
function parseDate(date: string): number; // 传入时间字符串, 返回时间戳
function parseDate(date: number): Date; // 传入时间戳, 返回 Date 对象
function parseDate(date: Date | string | number): string | number | Date {
  if (date instanceof Date) return date.toLocaleString();
  if (typeof date === "string") return new Date(date).getTime();
  return new Date(date);
}

使用 interface 定义函数重载

interface ParseDate {
  (date: Date): string;
  (date: string): number;
  (date: number): Date;
}
const parseDate: ParseDate = (date) => {
  if (date instanceof Date) return date.toLocaleString();
  if (typeof date === "string") return new Date(date).getTime();
  return new Date(date);
};

泛型

泛型是指在定义函数、接口或类的时候, 不预先指定具体的类型, 而是在使用的时候再指定类型, 就像函数参数一样.
泛型的使用有利于提升代码的可复用性和可维护性.

在 interface 中使用

interface People<T> {
  name: string;
  age: number;
  job: T;
}

enum JOB {
  FRONTEND_DEVELOPER,
  BACKEND_DEVELOPER,
}

const eno: People<JOB> = {
  name: "eno",
  age: 18,
  job: JOB.FRONTEND_DEVELOPER,
};

在函数中使用

const fetchData = <P, T>(url: string, params: P): T => {
  const res = http.get(url, params);
  return res.data as T;
};
const list: number[] = fetchData("url", "str"); // const fetchData: (url: string, params: string) => number[]

使用默认值

const fetchData = <P = string, T = number[]>(url: string, params: P): T => {
  const res = http.get(url, params);
  return res.data as T;
};
const list: number[] = fetchData("url", "str"); // const fetchData: (url: string, params: string) => number[]

对泛型的限制

使用 extends 可以限制泛型的类型

interface HttpResponseData {
  code: string;
  message: string;
}
// 限定 params 的类型 P 必须符合 object, 返回值 T 的类型必须符合 HttpResponseData
const fetchData = <P extends object, T extends HttpResponseData>(
  url: string,
  params: P
): T => {
  const res = http.get(url, params);
  return res.data as T;
};

// 错误写法
fetchData<string, number[]>("url", "str"); // 报错: string 不满足约束 object, number[] 不满足约束 HttpResponseData

// 正确写法
interface Params {
  search: string;
}
interface ResponseData {
  code: string;
  data: number[];
  message: string;
}
fetchData<Params, ResponseData>("url", { search: "search value" }); // const fetchData: (url: string, params: Params) => ResponseData

交叉类型和联合类型

交叉类型

将多个类型合并成一个类型, 新类型同时具有多个类型的所有属性

interface Person {
  name: string;
  age: number;
}
interface People {
  name: string;
  gender: "male" | "female";
}
type Human = Person & People; // {name: string; age: number; gender: 'male' | 'female'}

const eno: Human = {
  name: "eno",
  age: 18,
  gender: "male",
};
联合类型

用于表示一个类型有可能是多个类型中的一个

`type UnionType = A | B;

联合类型 UnionType 表示一个类型可能是 A 类型也可能是 B 类型.

当访问联合类型数据属性时, 只能访问多个类型中共同拥有的属性, 只要有一个类型不具备该属性, 就无法从联合类型数据中直接获取属性值. 所以使用联合类型时经常使用断言来确定类型

interface Teacher {
  name: string;
  workCode: string;
}
interface Student {
  name: string;
  studentCode: string;
}
type Member = Teacher | Student;

const eno: Member = {
  name: "eno",
  workCode: "12138",
};
const printMember = (member: Member) => {
  console.log(member.name); // 正常
  console.log(member.workCode); // 报错
  console.log(member.studentCode); // 报错

  if ((member as Teacher).workCode) {
    const { name, workCode } = member as Teacher; // 使用断言告诉 TS 这个数据是 Teacher 类型
    return `My name is ${name}, I am teacher, code: ${workCode}`;
  } else {
    const { name, studentCode } = member as Student;
    return `My name is ${name}, I am student, code: ${studentCode}`;
  }
};

内置高级类型

类型说明
Record使用两个泛型表示 key 和 value 来构建对象类型
Readonly把所有属性变为只读
Partial把必选变为可选选
Required把可选变为必选
Pick从一个类型中选择一些属性, 构建新类型
Omit从一个类型中剔除一些属性, 构建新类型
Exclude从一个联合类型中剔除一些类型
Extract从一个联合类型中提取一些类型
Parameters获取函数参数类型
ReturnType获取函数返回值类型
InstanceType获取构造函数返回值类型
ConstructorParameters用于提取构造器参数的类型
Awaited用于获取 Promise 类型的返回值类型
Uppercase用于将字符串类型转换为大写
Lowercase用于将字符串类型转换为小写
Capitalize用于将字符串类型的首字母转换为大写
Uncapitalize用于将字符串类型的首字母转换为小写
NonNullable用于从联合类型中剔除 null 和 undefined
ThisType用于指定函数中 this 的类型
// Record
type IObject = Record<string, number>;
// 等价于
interface IObject {
  [key: string]: number;
}

// Readonly
interface Person {
  name: string;
  age: number;
  job: {
    title: string;
    salary: number;
  };
}
type ReadonlyPerson = Readonly<Person>;
// 等价于
interface ReadonlyPerson {
  readonly name: string;
  readonly age: number;
  readonly job: {
    title: string;
    salary: number;
  };
}

// Partial
interface Person {
  name: string;
  age: number;
  job: {
    title: string;
    salary: number;
  };
}
type PartialPerson = Partial<Person>;
// 等价于
interface PartialPerson {
  name?: string;
  age?: number;
  job?: {
    title?: string;
    salary?: number;
  };
}
// Required
interface Person {
  name?: string;
  age?: number;
  job?: {
    title?: string;
    salary?: number;
  };
}
type RequiredPerson = Required<Person>;
// 等价于
interface RequiredPerson {
  name: string;
  age: number;
  job: {
    title?: string;
    salary?: number;
  };
}

// Pick
interface Person {
  name: string;
  age: number;
  job: {
    title: string;
    salary: number;
  };
}
type PickPerson = Pick<Person, "name" | "age">;
// 等价于
interface PickPerson {
  name: string;
  age: number;
}

// Omit
interface Person {
  name: string;
  age: number;
  job: {
    title: string;
    salary: number;
  };
}
type OmitPerson = Omit<Person, "name" | "age">;
// 等价于
interface OmitPerson {
  job: {
    title: string;
    salary: number;
  };
}
// Exclude
type Status = "success" | "failed" | "pending" | "loading";
type NotFailedStatus = Exclude<Status, "failed">; // "success" | "pending" | "loading"

// Extract
type Status = "success" | "failed" | "pending" | "loading";
type OtherStatus = "success" | "failed" | "done";
type ExtractStatus = Extract<Status, OtherStatus>; // "success" | "failed"

// Parameters & ReturnType
type Func = (name: string, age: age) => { name: string; age: number };
type FuncParams = Parameters<Func>; // [name: string, age: number]
type FuncReturnType = ReturnType<Func>; // {name: string; age: number}

// ConstructorParameters & InstanceType
interface PersonConstructor {
  new (name: string, age: number): { name: string; age: number };
}
type PersonConstructorParameters = ConstructorParameters<PersonConstructor>; // [name: string, age: number]
type PersonInstanceType = InstanceType<PersonConstructor>; // { name: string; age: number}

// Awaited
interface Person {
  name: string;
  age: number;
}
type PromisePerson = Promise<Person>;
type _Person = Awaited<PromisePerson>; // Person

// Uppercase & Lowercase & Capitalize & Uncapitalize
const name: Uppercase<string> = "ENO";
const name: Lowercase<string> = "eno";
const name: Capitalize<string> = "Eno";
const name: Uncapitalize<string> = "eno-Zeng";

// NonNullable
type Data = string | null | undefined;

type NonNullData = NonNullable<Data>; // string

// ThisType
interface Data {
  count: number;
}
interface Methods {
  getCount: () => number;
}
interface Store {
  data: Data;
  methods: Methods & ThisType<Data & Methods>;
}
const store: Store = {
  data: { count: 0 },
  methods: {
    getCount() {
      return this.count;
    },
  },
};
store.methods.getCount.call({ ...store.data, ...store.methods });

TS 类型体操与 extends & infer

什么是类型体操
TS 类型体操是 TypeScript 社区里的一个术语, 指的是利用 TS 类型推导从而获取一个新的类型的过程. 可以理解成在类型层面的逻辑运算.

extends
上面泛型中有提到 extends 关键字, 它可以用于泛型的类型约束, 此外 extends 也是 interface 继承的关键字. extends 还有更高级的用法就是用于条件类型判断, 条件类型是 TS 中非常重要的概念, 它可以根据类型的特性来进行类型推导.

举个例子: 假设一个 getInfo 函数, 参数可能是 User 或 Admin 类型, 根据不同的类型返回 UserInfo 或 AdminInfo 类型.

interface User {}
interface Admin {}
interface UserInfo {}
interface AdminInfo {}
const getInfo = <T extends User | Admin>(
  user: T
): T extends User ? UserInfo : AdminInfo => {};
const userInfo = getInfo({} as User); // UserInfo
const adminInfo = getInfo({} as Admin); // AdminInfo

infer

infer 用于在类型推导中声明一个待推导的局部类型变量. infer 语句作为**条件类型(extends ? :)**的子语句, 只能在条件类型语句中使用.

infer 在 TS 的类型运算中有很重要的作用

举个例子: 比如我们有一个 List 类型, 我们想要获取 List 类型, 我们想要提取 List 类型中的元素类型, 可以使用 infer 语句.

type List = Array<number>;

type ListItem = List extends Array<infer Item> ? Item : never; // number

实现提取 Promise 类型中的返回值类型

TS 提供了 Awaited 类型, 用于获取 Promise 类型的返回值类型. 我们可以使用 infer 语句来实现 Awaited 类型.

type AwaitedPromise<T> = T extends Promise<infer P> P extends Promise<infer U> ? U : P : T;

在 extends 的条件语句中, 使用 infer 推导出 Promise 的泛型 P, 若 P 是 Promise 类型, 则继续递归调用 AwaitedPromise, 否则返回 P.

实现深度 readonly

TS 提供了 Readonly 类型, 用于将一个类型的所有属性变为只读. 但是对于对象嵌套的情况, Readonly 类型只能将第一层属性变为只读, 对于嵌套的属性, Readonly 类型无法将其变为只读.

interface Person {
  name: string;
  age: number;
  job: {
    title: string;
    salary: number;
  };
}

type ReadonlyPerson = Readonly<Person>;
// 等价于
interface ReadonlyPerson {
  readonly name: string;
  readonly age: number;
  readonly job: {
    title: string;
    salary: number;
  };
}

使用 Readonly 处理 Person 类型, 可以将 Person 类型的所有属性变为只读, 但是对于 job 属性这种对象类型属性, 其属性并不会变为只读

type DeepReadonly<T extends object> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};

type ReadonlyPerson = DeepReadonly<Person>;
// 等价于
interface ReadonlyPerson {
  readonly name: string;
  readonly age: number;
  readonly job: {
    readonly title: string;
    readonly salary: number;
  };
}

通过 extends 语句判断 T[K] 是否为 object 类型, 如果是, 则递归调用 DeepReadonly 类型, 来实现深层的 readonly.

实现下划线转驼峰

type CamelCase<T extends string> = T extends `${infer Left}_${infer Right}`
  ? `${Left}${Capitalize<Right>}` extends `${infer L}_${infer R}`
    ? CamelCase<`${L}_${R}`>
    : `${Left}${Capitalize<Right>}`
  : T;

type CamelCasePerson = CamelCase<"name_age_job">; // "nameAgeJob"
### TypeScript 入门指南与常见问题解决方案 #### 什么是 TypeScriptTypeScript 是一种由微软开发的开源编程语言,它是 JavaScript 的一个超集,扩展了 JavaScript 的功能并引入了静态类型系统。通过这种方式,TypeScript 提供了更强的类型检查能力,在编写代码阶段就能发现潜在的错误[^5]。 #### 如何安装 TypeScript? 要开始使用 TypeScript,可以先通过 npm 安装其命令行工具: ```bash npm install -g typescript ``` 安装完成后可以通过 `tsc --version` 来验证是否成功安装[^3]。 #### TypeScript 的基本语法 TypeScript 支持多种数据类型,包括原始值类型和复杂类型。以下是常见的几种类型定义方式: - **原始值类型** ```typescript let num: number = 10; let str: string = "Hello"; let bool: boolean = true; ``` 这些类型的声明是可选的,因为 TypeScript 能够通过上下文推断出变量的类型[^4]。 #### 使用 TypeScript 编写模块化代码 TypeScript 支持 ES6 模块标准,允许开发者将代码分割为多个文件以便于管理。例如: ```typescript // math.ts export function add(a: number, b: number): number { return a + b; } // main.ts import { add } from './math'; console.log(add(2, 3)); ``` 以上代码展示了如何导出函数以及如何在其他文件中导入该函数[^5]。 #### 解决方案:如何理解和使用项目中的风格指南? 为了使团队协作更加高效,遵循统一的编码规范是非常重要的。TypeScript 风格指南提供了许多关于命名约定、代码结构等方面的建议。例如,推荐使用 PascalCase 表示类名,而 camelCase 则用于方法和属性名称[^2]。 #### 工具支持 得益于丰富的插件生态,大多数现代集成开发环境 (IDE),如 Visual Studio Code 和 WebStorm,都内置了对 TypeScript 的良好支持。这不仅限于语法高亮显示,还包括实时错误检测、智能感知等功能[^5]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

eno_zeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值