TS进阶之infer

本文介绍 TypeScript 中 infer 的使用方法,包括推断数组、元组、函数参数与返回值类型等,并提供综合案例加深理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前一篇文章我们有介绍条件类型,这篇文章我们来介绍下非常实用的infer的使用方法。

使用介绍

我们上篇文章中介绍了条件类型的基本语法是:

 T extends U ? X : Y;

如果占位符类型U是一个可以被分解成几个部分的类型,譬如数组类型,元组类型,函数类型,字符串字面量类型等。这时候可以通过infer来获取U类型中某个部分的类型。

infer语法的限制如下:

  1. infer只能在条件类型的 extends 子句中使用
  2. infer得到的类型只能在true语句中使用, 即X中使用

推断数组(或者元组)的类型

使用方法

type InferArray<T> = T extends (infer U)[] ? U : never;

(infer U)和平时常写的string[]number[]等等是不是很像?这里就是通过(infer U)来获取数组对应的类型。

案例

type I0 = InferArray<[number, string]>; // string | number
type I1 = InferArray<string[]>; // string
type I2 = InferArray<number[]>; // number

推断数组(或者元组)第一个元素的类型

使用方法

type InferFirst<T extends unknown[]> = T extends [infer P, ...infer _] ? P : never

[infer P, ... infer _]infer P获取的是第一个元素的类型,而...infer _获取的是数组其他剩余元素的数组类型;
特别说明下,我们例子汇总不需要使用其他元素的类型,所以用_

案例

type I3 = InferFirst<[3, 2, 1]>; // 3

推断数组(或者元组)最后一个元素的类型

使用方法

type InferLast<T extends unknown[]> = T extends [... infer _, infer Last] ? Last : never;

这个和推断数组第一个元素的类型类似,...infer _获取的是最后一个元素之前的所有元素类型,infer Last获取的是最后一个元素的类型。

案例

type I4 = InferLast<[3, 2, 1]>; // 1

推断函数类型的参数

使用方法

type InferParameters<T extends Function> = T extends (...args: infer R) => any ? R : never;

...args 代表的是函数参数组成的元组, infer R代表的就是推断出来的这个函数参数组成的元组的类型。

案例

type I5 = InferParameters<((arg1: string, arg2: number) => void)>; // [string, number]

推断函数类型的返回值

使用方法

type InferReturnType<T extends Function> = T extends (...args: any) => infer R ? R : never;

和前面的推断函数类型的参数类似,=> 后面的infer R代表的就是推断出来的函数的返回值类型。

案例

type I6 = InferReturnType<() => string>; // string

推断Promise成功值的类型

使用方法

type InferPromise<T> =  T extends Promise<infer U> ? U : never;

案例

type I7 = InferPromise<Promise<string>>; // string

推断字符串字面量类型的第一个字符对应的字面量类型

使用方法

type InferString<T extends string> = T extends `${infer First}${infer _}` ? First : [];

案例

type I8 = InferString<"Johnny">; // J

综合案例

接下来我举一些综合性的例子,我不介绍这些例子实现的功能,大家来感受下infer的使用技巧,看看是否能一眼看出来实现的功能:

type Shift<T> = T extends [infer L, ...infer R]? [...R] : [];
type Pop<T extends any[]> = T extends [...infer L, infer R] ? [...L] : [];
type Reverse<T extends unknown[], U extends unknown[] = []> = [] extends T
  ? U
  : T extends [infer L, ...infer R]
  ? Reverse<R, [L, ...U]>
  : U;
type FlipArguments<T extends Function> = T extends (...arg: infer R) => infer S ? (...arg : Reverse<[...R]>) => S : T;
type StartsWith<T extends string, U extends string> = T extends `${U}${infer R}` ? true : false;
type TrimLeft<S extends string> = S extends `${infer L}${infer R}`
  ? L extends ' ' | '\n' | '\t'
    ? TrimLeft<R>
    : S
  : '';
type Trim<S extends string> = S extends `${' ' | '\t' | '\n'}${infer R}`
  ? Trim<R>
  : S extends `${infer L}${' ' | '\t' | '\n'}`
  ? Trim<L>
  : S;
type StringToUnion<T extends string, U = never> = T extends ''
  ? U
  : T extends `${infer L}${infer R}`
  ? StringToUnion<R, U | L>
  : U;

这些例子中涉及到两个没有介绍的知识点:模版字面量类型递归类型,如果对这两个知识点不太懂的可以参考其他文章。这两个知识点我后面也会介绍

<think>嗯,用户问的是关于TypeScript的面试题,我需要先整理一下常见的TS面试问题,然后分门别类地解释清楚。首先,应该从基础概念开始,比如TypeScript和JavaScript的区别,这是必问的问题。然后类型系统,包括静态类型类型注解和类型推断,这些都需要详细说明。接下来是高级类型,比如联合类型、交叉类型类型别名、接口和泛型,这些都是TS的核心特性。 然后,用户可能还会问到实际应用中的问题,比如声明文件的作用,如何编写.d.ts文件,模块和命名空间的区别,装饰器的使用,以及枚举类型的特点。工具方面,tsconfig.json的配置也是常见的考点,需要解释主要配置的作用。 此外,进阶问题比如类型保护、类型断言和类型声明的区别、工具类型(Partial、Readonly等)的应用,以及条件类型infer关键字的使用,这些都是高级话题,需要深入讲解。实际开发中的问题,比如如何处理第三方库的类型定义,类型兼容性的规则,any和unknown的区别,以及泛型约束的应用,都是面试中常被问到的。 在准备答案时,要确保每个问题都有清晰的解释和示例,尤其是代码示例要用正确的LaTeX格式,行内公式用$...$,独立公式用$$...$$。比如在解释联合类型时,可以举一个变量可能是string或number的例子,用TypeScript代码展示。同时,要避免使用Markdown格式,保持回答结构清晰,分点说明。 还要注意用户可能需要了解面试题的范围和深度,所以需要覆盖从基础到进阶的内容,帮助用户全面准备。另外,要确保语言口语化,避免过于技术化的术语堆砌,让解释更易于理解。最后,检查所有LaTeX语法是否正确,符合用户提供的系统级指令要求。</think>以下是一些常见的TypeScript面试题及解析,分为基础、进阶和实战应用三个部分: --- ### 一、基础概念 1. **TypeScript与JavaScript的区别** - TypeScript是JavaScript的超集,添加了静态类型系统 - 编译时类型检查(JavaScript是动态类型) - 支持ES6+特性并兼容旧浏览器(通过编译转换) 2. **静态类型的作用** - 提高代码可读性:$let age: number = 25$ 明确变量用途 - 编译时错误检查:避免运行时类型错误 - 增强IDE支持:自动补全、代码导航等 3. **类型注解 vs 类型推断** ```typescript // 类型注解(显式) let name: string = "Alice"; // 类型推断(隐式) let score = 100; // 自动推断为number ``` --- ### 二、核心特性 4. **联合类型与交叉类型** ```typescript // 联合类型 (|) type ID = string | number; // 交叉类型 (&) type Named = { name: string }; type Aged = { age: number }; type Person = Named & Aged; // {name: string, age: number} ``` 5. **接口(interface)与类型别名(type)的区别** - 接口可合并声明,类型别名不可 - 类型别名能表示更复杂的类型(如联合类型) - 推荐优先使用接口定义对象结构 6. **泛型应用场景** ```typescript // 定义可复用类型 function identity<T>(arg: T): T { return arg; } // 约束泛型类型 interface Lengthwise { length: number; } function loggingIdentity<T extends Lengthwise>(arg: T): T { console.log(arg.length); return arg; } ``` --- ### 三、进阶问题 7. **类型守卫(Type Guards)** ```typescript // typeof 守卫 function padLeft(value: string | number) { if (typeof value === "number") { return value.toFixed(2); } return value.padStart(10); } // 自定义类型守卫 function isFish(pet: Fish | Bird): pet is Fish { return (pet as Fish).swim !== undefined; } ``` 8. **声明合并(Declaration Merging)** - 接口自动合并: ```typescript interface Box { height: number; } interface Box { width: number; } // 最终合并为 { height: number; width: number; } ``` 9. **工具类型实现原理** ```typescript type Partial<T> = { [P in keyof T]?: T[P] }; // 所有属性变为可选 type Pick<T, K extends keyof T> = { [P in K]: T[P] }; // 选特定属性 ``` --- ### 四、实战问题 10. **如何处理第三方库缺少类型定义?** - 创建声明文件:`*.d.ts` - 使用`declare module 'library-name'`语法 - 或安装社区维护的`@types/*`包 11. **类型兼容性的结构化原则** TypeScript使用"鸭式辨型法":只要结构匹配即兼容 ```typescript interface Point { x: number; y: number; } let p = { x: 1, y: 2, z: 3 }; let point: Point = p; // 兼容,因包含x,y ``` 12. **any vs unknown的区别** - `any`会跳过类型检查,可能引发运行时错误 - `unknown`更安全,使用时需显式类型断言或收窄类型 --- ### 五、配置与工具 13. **tsconfig.json核心配置** ```json { "compilerOptions": { "target": "ES6", // 编译目标版本 "module": "CommonJS", // 模块系统 "strict": true, // 启用严格模式 "outDir": "./dist" // 输出目录 } } ``` --- 以上内容覆盖了TypeScript面试常见考点,建议结合实际编码练习加深理解。在准备时可重点关注:类型系统设计、泛型应用、类型工具的实现原理,以及实际目中的类型方案设计。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值