TypeScript 类型体操练习笔记(二)

作者:我不吃饼干日期:2026/2/18

进度(90 /188)

其中标记 ※ 的是我认为比较难或者涉及新知识点的题目

刷题也许没有什么意义,但是喜欢一个人思考一整天的灵光一现,也喜欢看到新奇的答案时的恍然大悟,仅此而已。

42. Medium - 1130 - ReplaceKeys ※

实现一个类型 ReplaceKeys,用于替换联合类型中的键,如果某个类型不包含该键则跳过替换。该类型接受三个参数。

一开始我只是想这么写,我想分布式条件类型 + Pick + Omit 来实现。

1type ReplaceKeys<U, T, Y> = U extends any 
2 ? Omit<U, T & keyof U> & Pick<Y, T & keyof U & keyof Y>
3 : any
4

理论上 case1 是能通过的,但是一直报错。然后我又试了一下,看来判断的 Equal 不认为这两种是相等的:

1type T1 = { a: number }
2type T2 = { b: number }
3type E = Equal<T1 & T2, { a: number, b: number }> // false
4

不过还是有办法的,我们可以通过一层映射把交叉类型拍平:

1type IntersectionToObj<T> = {
2  [K in keyof T]: T[K]
3}
4type E1 = Equal<IntersectionToObj<T1 & T2>, { a: number, b: number }> // true
5

不过我试了下第二个 case 还是不太好实现,那就直接用映射类型来解决。

利用分布式特性处理联合元素,然后遍历 U 的属性然后按要求进行处理即可。

1type ReplaceKeys<U, T, Y> = U extends any
2  ? {
3    [K in keyof U]: K extends T ? (K extends keyof Y ? Y[K] : never) : U[K]
4  }
5  : never // 不会进入这个分支
6

但是看到别人的答案我又开始困惑了:

1type ReplaceKeys<U, T, Y> = {
2  [K in keyof U]: K extends T ? (K extends keyof Y ? Y[K] : never) : U[K]
3}
4

查了半天只有这个 pr官方文档里也没有明确说明。

形如 { [P in keyof T]: X } 的映射类型(其中 T 是类型参数)被称为 isomorphic mapped type(同构映射类型),因为它会产生一个与 T 具有相同结构的类型。通过此 PR,我们使同构映射类型的实例化在联合类型上具有分布性。

43. Medium - 1367 - Remove Index Signature 移除索引签名 ※

实现 RemoveIndexSignature<T>,移除一个对象类型的索引签名。

索引签名(Index Signature) 是 TypeScript 中用于描述对象中未明确声明的属性的类型。它允许你定义一个对象可以有任意数量的属性,只要这些属性的键和值符合指定的类型。

1interface StringDictionary {
2  [key: string]: string;  // 索引签名
3  // 表示该对象可以有任意多个属性,键必须是 string 类型,值也必须是 string 类型。
4}
5

和索引签名对应的是具体属性,这两种也可以混合使用,但是具体属性的类型必须是索引签名类型的子类型:

1interface MixedType {
2  // 具体属性
3  name: string;
4  age: number;
5  // 索引签名
6  [key: string]: string | number;  // 必须包含具体属性的类型
7}
8

要处理这个问题,就要针对索引签名的特点,他是一个宽泛的类型(string/number/symbol),而具体属性是一个字面量类型,比如 "name" ,我们依次判断它是否为 stringnumbersymbol 都不是则证明是具体属性,否则为索引签名。

1type RemoveIndexSignature<T> = {
2  [K in keyof T as
3  string extends K 
4    ? never
5    : number extends K 
6      ? never
7      : symbol extends K 
8        ? never
9        : K
10  ]: T[K]
11}`
12

在评论区看到一个很天才的解法

1type RemoveIndexSignature<T, P = PropertyKey> = {
2  [K in keyof T as P extends K? never : K extends P ? K : never]: T[K]
3}
4

其中 PropertyKey 上是 TypeScript 的内置类型 type PropertyKey = string | number | symbol;。它的判断过程如下:

1P extends K ? never : (K extends P ? K : never)  /* P = string | number | symbol */
2
3// becomes
4(string | number | symbol) extends K ? never : (K extends P ? K : never)
5
6// becomes
7| string extends K ? never : (K extends string ? K : never)
8| number extends K ? never : (K extends number ? K : never)
9| symbol extends K ? never : (K extends symbol ? K : never)
10

本质上和我们上面的写法是一样的,但是利用条件类型的分布性,一下子判断了三种类型。୧(๑•̀◡•́๑)૭

44. Medium - 1978 - Percentage Parser 百分比解析器

实现类型 PercentageParser。根据规则 /^(\+|\-)?(\d*)?(\%)?$/ 匹配类型 T

匹配的结果由三部分组成,分别是:[正负号, 数字, 单位],如果没有匹配,则默认是空字符串。

1type Sign = '+' | '-'
2type PercentageParser<A extends string> =
3  A extends `${infer F}%`
4  /** 存在 % */
5    ? F extends `${infer S extends Sign}${infer N}`
6      ? [S, N, '%']
7      : ['', F, '%']
8  /** 不存在 % */
9    : A extends `${infer S extends Sign}${infer N}`
10      ? [S, N, '']
11      : ['', A, '']
12

题目不难,加几个分支判断就可以了。或者这样写优雅一点(大概):

1type SignParser<A extends string> = A extends `${infer S extends '+' | '-'}${infer N}` ? [S, N] : ['', A]
2type PercentageParser<A extends string> = A extends `${infer F}%` ? [...SignParser<F>, '%'] : [...SignParser<A>, '']
3

45. Medium - 2070 - Drop Char 删除字符

从字符串中剔除指定字符。

1type DropChar<S, C> = S extends `${infer F}${infer R}`
2  ? F extends C 
3    ? DropChar<R, C>
4    : `${F}${DropChar<R, C>}`
5  : ''
6

没有新的知识点,简单题。

46. Medium - 2257 - MinusOne 减一 ※

给定一个正整数作为类型的参数,要求返回的类型是该数字减 1。

有点意思的一道题目,没有新的知识点,但是类似于算法中的模拟题。需要递归加不同情况的判断,复杂度较高。

我先想到了一个比较搞的办法,生成长度为 T 的数组,然后移除一个元素,再获取数组长度。

1type MakeArray<T extends number, R extends any[] = []> =
2  R['length'] extends T ? R : MakeArray<T, [...R, any]>
3type MinusOne<T extends number> = 
4  MakeArray<T> extends [infer _F, ...infer R] ? R['length'] : never
5

1000 以内是可行的,但是再大就会出现错误:

1type A = MakeArray<1101> // error: 类型实例化过深,且可能无限。ts(2589)
2

那么只能换一种方法,通过模拟减法的方式实现,枚举最后一位即可,如果最后一位大于 0 则只需要操作最后一位,否则需要递归处理:

1type MinusOne2String<T extends string> =
2  T extends `${infer F}0` // 如果最后一位是0,则把此位改为9,然后递归处理(题目限定了是正数)
3  ? `${MinusOne2String<F>}9`
4  : T extends `${infer F}9` // 其他情况直接把最后一位减一
5  ? `${F}8`
6  : T extends `${infer F}8`
7  ? `${F}7`
8  : T extends `${infer F}7`
9  ? `${F}6`
10  : T extends `${infer F}6`
11  ? `${F}5`
12  : T extends `${infer F}5`
13  ? `${F}4`
14  : T extends `${infer F}4`
15  ? `${F}3`
16  : T extends `${infer F}3`
17  ? `${F}2`
18  : T extends `${infer F}2`
19  ? `${F}1`
20  : T extends `${infer F}1`
21  ? `${F}0`
22  : '0'
23// 100-1=099 这种情况需要删除前导零
24type removeLeadZero<T extends string> = 
25  T extends '0' ? '0' : T extends `0${infer R}` ? removeLeadZero<R> : T 
26// 删除前导零后,转换为数字类型
27type MinusOne<T extends number> = 
28  removeLeadZero<MinusOne2String<`${T}`>> extends `${infer X extends number}` ? X : 0
29

47. Medium - 2595 - PickByType

T 中选择可赋值给 U 的属性类型集合。

1type PickByType<T, U> = {
2  [K in keyof T as T[K] extends U ? K : never ]: T[K]
3}
4

送分题,知识点前面的题目都有涉及。

48. Medium - 2688 - StartsWith

实现 StartsWith<T, U>,接收两个 string 类型参数,然后判断 T 是否以 U 开头,根据结果返回 truefalse

1type StartsWith<T extends string, U extends string> =
2  T extends `${U}${infer F}` ? true : false
3

送分题+1,模板字符串类型基础。

49. Medium - 2693 - EndsWith

实现 EndsWith<T, U>,接收两个 string 类型参数,然后判断 T 是否以 U 结尾,根据结果返回 truefalse

1type EndsWith<T extends string, U extends string> =
2  T extends `${string}${U}` ? true : false
3

50. Medium - 2757 - PartialByKeys

实现一个通用的 PartialByKeys<T, K>,它接收两个类型参数 TK

K 指定应设置为可选的 T 的属性集。当没有提供 K 时,它就和普通的 Partial<T> 一样使所有属性都是可选的。

前面已经讲过 IntersectionToObj 这个小技巧,这里就比较简单了,其中 Partial 是内置的工具类型,可以把一个对象类型的全部属性都变成可选。

1type IntersectionToObj<T> = {
2  [K in keyof T]: T[K]
3}
4
5type PartialByKeys<T , K extends keyof T = keyof T> = 
6  IntersectionToObj< Omit<T, K> & Partial<Pick<T, K>> >
7

51. Medium - 2759 - RequiredByKeys

实现一个通用的 RequiredByKeys<T, K>,它接收两个类型参数 TK

K 指定应设为必选的 T 的属性集。当没有提供 K 时,它就和普通的 Required<T> 一样使所有的属性成为必选的。

PartialByKeys 本质上没有什么区别,Required 也是内置的工具类型,可以把一个对象类型的全部属性,用于将一个类型 T 中的所有属性转换为‌必填属性(即移除其可选性 ?)。

1type IntersectionToObj<T> = {
2  [K in keyof T]: T[K]
3}
4type RequiredByKeys<T, K extends keyof T = keyof T> =
5  IntersectionToObj<Omit<T, K> & Required<Pick<T, K>>>
6

52. Medium - 2793 - Mutable ※

实现一个通用的类型 Mutable<T>,使类型 T 的全部属性可变(非只读)。

这题不难,但是涉及到映射类型的一个语法,之前没有涉及过。mapped-types

1type Mutable<T extends object> ={
2  -readonly [K in keyof T]: T[K]
3}
4

53. Medium - 2852 - OmitByType

从类型 T 中选择不可赋值给 U 的属性成为一个新的类型。

直到了 asMapped Types 的用法,这也很简单,和之前的 Omit 没什么区别。

1type OmitByType<T, U> = {
2  [K in keyof T as T[K] extends U ? never : K]: T[K]
3}
4

54. Medium - 2946 - ObjectEntries

实现 Object.entries 的类型版本。

首先这题需要应用分布式条件类型,所以需要先构造一个由类型key组成的联合类型 U 然后 U extends ... 触发分布式。

1type ObjectEntries<T, U = keyof T> =
2  U extends keyof T ? [U, T[U]] : never
3

不过有个case 过不去。

1type eq = Equal<ObjectEntries<Partial<Model>>, ModelEntries> // false
2type o = ObjectEntries<Partial<Model>>
3// ["name", string | undefined] | ["age", number | undefined] | ["locations", string[] | null | undefined]
4

可以看到由于 Partial 导致每个类型都多了一个 undefined。很明显这里需要 Required,但是需要先了解一下它的特性。

1type r1 = Required<{ key?: undefined }> // {key: never}
2type r2 = Required<{ key: undefined }> // {key: undefined}
3type r3 = Required<{ key: string | undefined }> // {key: string | undefined}
4type r4 = Required<{ key?: string | undefined }>  // {key:string}
5

可以看到在存在 ? 时,Required 会删除类型中的 undefined,否则不会。

而此题的要求是:如果类型存在 ? 就删除 undefined,但是如果类型只有 undefined 则不处理。我只能说,题本身不难,但是描述的不清楚,只能看用例。

1type ObjectEntries<T, U = keyof T> =
2  U extends keyof T 
3    ? [U, [T[U]] extends [undefined] ? undefined : Required<T>[U]] 
4    : never
5

55. Medium - 3062 - Shift

实现类型版本的 Array.shift

1type Shift<T extends any[]> = T extends [infer F, ...infer R] ? [...R] : []
2

infer 的基础应用,在最前面的 First of Array 就了解过了。

56. Medium - 3188 - Tuple to Nested Object

给一个只包含字符串类型的元组 T 和一个类型 U 递归构建一个对象。

1type TupleToNestedObject<T extends string[], U> = 
2  T extends [infer F extends string, ...infer R extends string[]] 
3    ? Record<F, TupleToNestedObject<R, U>> : U
4

每次提取数组中第一个元素,然后把该元素作为键,递归构造的对象作为值。

57. Medium - 3192 - Reverse

实现类型版本的数组反转 Array.reverse

1type Reverse<T extends any[]> = T extends [infer F, ...infer R] 
2  ? [...Reverse<R>, F]
3  : []
4

使用递归的方式,每次都把第一个元素移到最后一个。

58. Medium - 3196 - Flip Arguments

实现 lodash 中 _.flip 函数的类型版本。

类型转换函数 FlipArguments<T> 要求函数类型 T,并返回一个新的函数类型,该类型具有与 T 相同的返回类型但参数顺序颠倒。

1type FlipArguments<T extends Function> = 
2  T extends (...args: infer P) => infer R ? (...args: Reverse<P>) => R : never
3

通过 infer 获取函数的参数和返回值,并且通过上一题实现的 Reverse 将参数反转。

59. Medium - 3243 - FlattenDepth

递归展开数组至指定深度

首先需要实现一个铺平一次的函数,这个比较简单

1type FlattenOnce<A extends any[]> = A extends [infer F, ...infer R]
2  ? F extends any[] ? [...F, ...FlattenOnce<R>] : [F, ...FlattenOnce<R>]
3  : []
4

TypeScript 中无法进行数字计算,我们可以通过邪修实现,这点我们在前面的 MinusOne 已经实现,所以这里直接引用 MinusOne 就可以了。

1type FlattenDepth<T extends any[], depth extends number = 1> =
2  depth extends 0 // 判断深度为零,则已经不需要铺平了
3  ? T
4  : FlattenOnce<T> extends T // 判断是否铺平前后的结果一致,一致则不需要再处理了
5    ? T 
6    : FlattenDepth<FlattenOnce<T>, MinusOne<depth>>
7

当然我们可以用之前在里面用过的用数组记录数字的方法,只不过 TypeScript 中数组长度有限制,当然这一题中是没有问题的,嵌套最多才 5 层

1type FlattenDepth<T extends any[], depth = 1, depArr extends any[] = []> = 
2  depArr['length'] extends depth
3    ? T 
4    : FlattenOnce<T> extends T ? T : FlattenDepth<FlattenOnce<T>, depth, [...depArr, any]>
5

60. Medium - 3326 - BEM style string

使用块(Block)、元素(Element)、修饰符(Modifier)命名(BEM)是 CSS 中类的一种流行命名约定。

例如,块组件表示为 btn,依赖于块的元素表示为 btn__price,更改块样式的修饰符表示为 btn-bigbtn__prise-warning

实现 BEM<B,E,M>,从这三个参数生成字符串并集。其中 B 是字符串文字,EM 是字符串数组(可以为空)。

1// 把A和B连接,先把B处理为联合类型,然后用S连接
2type JoinWithSeparator<A extends string, B extends string[], S extends string, B2Union extends string = B[number]> = 
3  B2Union extends any ? `${A}${S}${B2Union}` : never
4
5type BEM<B extends string, E extends string[], M extends string[]> = 
6  E['length'] extends 0 // 判断E是否为空
7    ? JoinWithSeparator<B, M, '--'> // 如果E为空, B和M连接
8    : M['length'] extends 0 // 判断M是否为空
9      ? JoinWithSeparator<B, E, '__'> // E不为空,M为空,把B和M连接
10      : JoinWithSeparator<JoinWithSeparator<B, E, '__'>, M, '--'> // 都不为空,先把B和E连接,然后再加上M
11

需要写一个辅助工具类型 JoinWithSeparator 用于连接一个字符和数组,逻辑有点小复杂,已经加了完整注释。

61. Medium - 3376 - InorderTraversal

实现二叉树中序遍历的类型版本。

如果会中序遍历二叉树这题就不难了,不会的可以先学学数据结构。

1type InorderTraversal<T extends TreeNode | null> = 
2 T extends null 
3   ? [] 
4   : [...InorderTraversal<T['left']>, T['val'], ...InorderTraversal<T['right']>]
5

比较麻烦的是,T extends null 语法无法判断第二个分支中 T 不为空,所以可以反过来,判断 T 是否为 TreeNode

1type InorderTraversal<T extends TreeNode | null> =
2  T extends TreeNode
3  ? [
4    ...InorderTraversal<T['left']>,
5    T['val'],
6    ...InorderTraversal<T['right']>
7  ] : []
8

62. Medium - 4179 - Flip

实现 just-flip-object 的类型版本(把类型的键和值类型反转)。

1type Flip<T> = {
2  [K in keyof T as T[K] extends number | string | boolean ? `${T[K]}` : never]: K
3}
4

为了保证 T[K] 类型正确,加了一个 extends number | string | boolean 的限制。

63. Medium - 4182 - Fibonacci Sequence 斐波那契序列 ※

实现一个通用的 Fibonacci<T>,它接受一个数字 T 并返回其相应的斐波纳契数

序列开始:1、1、2、3、5、8、13、21、34、55、89、144...

首先斐波纳契公式 f(n)=f(n-1)+f(n-2) 可以递归实现。由于 TypeScript 类型无法使用加法,所以我们通过数组的元素个数来变向进行计算,至于减法可以复用之前实现的 MinusOne

1type FibonacciArray<T extends number, A extends any[] = []> =
2    T extends 1
3    ? [any]
4    : T extends 2
5      ? [any]
6      : [...FibonacciArray<MinusOne<MinusOne<T>>>, ...FibonacciArray<MinusOne<T>>]
7type Fibonacci<T extends number> = FibonacciArray<T>['length']
8

看了下别人的答案,优化空间还是很大的,下面是正向计算,Index 表示计算到了第 n 个数字,Cur 表示 f(n)Prev 表示 f(n-1)

1type Fibonacci<
2  T extends number,
3  Index extends any[] = [any, any],
4  Cur extends any[] = [any],
5  Prev extends any[] = [any]
6> =
7  T extends 1 | 2
8    ? 1
9    : Index['length'] extends T
10      ? Cur['length']
11      : Fibonacci<T, [...Index, any], [...Cur, ...Prev], Cur>
12

64. Medium - 4260 - AllCombinations ※

实现 AllCombinations<S> 类型,该类型返回使用 S 中的字符所组成的所有字符串,每个字符最多使用一次。

1type AllCombinations<S extends string, P extends string = ''> =
2  S extends `${infer F}${infer R}` 
3    ? '' | F | `${F}${AllCombinations<`${R}${P}`>}` // S[0] 开头的所有排列情况
4      | AllCombinations<R, `${P}${F}`> // 除了 S[0] 开头以外的所有情况
5    : ''
6

很有意思的题目,实现一个字符串中字符的所有组合。我的解法是 AllCombinations<S, P> 表示获取字符串 ${S}${P} 中,以 S 每个字母开头的全排列组合。

所以 AllCombinations<S, ''> 就是答案,而它等于 S[0] 为开头的所有情况,再加上 AllCombinations<S.split(1), S[0]>(伪代码示例)

S[0] 为开头的所有情况,就是求 S[0] 连接剩余字符的全排列,也就是 AllCombinations<S.split(1), ''>

65. Medium - 4425 - Greater Than

在本次挑战中,你需要实现一个类似 T > U 的类型: GreaterThan<T, U>负数无需考虑。

这种题我可以说不难,就是有点恶心。我不喜欢!下面的代码我加了注释,应该可以看懂。

1type LengthOfString<S extends string> = Split<S>['length'];
2type FirstOfString<S extends string> = S extends `${infer F}${infer R}`
3  ? F
4  : never;
5type RestOfString<S extends string> = S extends `${infer F}${infer R}`
6  ? R
7  : never;
8
9type Split<S extends string> = S extends `${infer F}${infer R}`
10  ? [F, ...Split<R>]
11  : [];
12
13// 比较10以内数字的大小
14type GreaterThanDigit<
15  T extends string,
16  U extends string,
17  D extends string = '9876543210'
18> = D extends `${infer F}${infer R}` // 从大到小依次比较每一个数字
19  ? F extends U // 如果先匹配了U 则证明T≤U 返回false
20    ? false
21    : F extends T // 如果先匹配了T 则证明T>U 返回true
22      ? true
23      : GreaterThanDigit<T, U, R> // 再尝试匹配下一个数字
24  : false;
25
26type GreaterThanString<
27  T extends string,
28  U extends string,
29  LEN_T extends number = LengthOfString<`${T}`>, // T的长度
30  LEN_U extends number = LengthOfString<`${U}`>, // U的长度
31  FIRST_T extends string = FirstOfString<`${T}`>, // T的长度
32  FIRST_U extends string = FirstOfString<`${U}`> // U的长度
33> = LEN_U extends LEN_T // 判断长度是否相同
34  ? LEN_T extends 1 // 判断相同,长度是否为1
35    ? GreaterThanDigit<FIRST_T, FIRST_U> // 长度为1 直接比较首位
36    : FIRST_T extends FIRST_U // 长度相同,且长度不为1,依次比较每一位,先判断首位
37      ? GreaterThanString<RestOfString<`${T}`>, RestOfString<`${U}`>> // 首位相同 则比较下一位
38      : GreaterThanDigit<FIRST_T, FIRST_U> // 首位不同,则比较大小
39  : GreaterThan<LEN_T, LEN_U>; // 如果长度不相同,则长度大的数字更大
40
41type GreaterThan<T extends number, U extends number> = GreaterThanString<
42  `${T}`,
43  `${U}`
44>;
45

66. Medium - 4471 - Zip

在这个挑战中,你需要实现一个类型 Zip<T, U>,其中 TU 必须是元组。

就是把所有元组中的第1项组成结果中的第1项,所有元组中的第2项组成结果中的第2项....所有元组中的第n项组成结果中的第n项,比较简单。

1type Zip<T extends any[], U extends any[]> = T extends [infer TF, ...infer TR]
2  ? U extends [infer UF, ...infer UR]
3    ? [[TF, UF], ...Zip<TR, UR>]
4    : []
5  : [];
6

67. Medium - 4484 - IsTuple ※

实现类型 IsTuple, 传入类型 T 并返回类型 T 是否为一个元组类型

A tuple type is another sort of Array type that knows exactly how many elements it contains, and exactly which types it contains at specific positions.

元组类型 是另一种“数组”类型,它确切地知道它包含多少元素,以及它在特定位置包含哪些类型。

T extends readonly any[] 判断 T 为数组和元素,添加 readonly 可以兼容 readonly [1] 这种情况。

根据定义可以知道,元组类型的长度是固定的,所以 Tuple['length'] 是一个具体的数字,而数组 A['length']number

因此,可以通过 number extends T['length'] 来判断 T 是否为元组而不是数组。

1type IsTuple<T> = [T] extends [never]
2  ? false
3  : T extends readonly any[]
4    ? number extends T['length']
5      ? false
6      : true
7    : false;
8

68. Medium - 4499 - Chunk

你知道 lodash 吗?Chunk 是其中一个非常有用的函数,现在让我们实现它。Chunk<T,N> 接受两个必需的类型参数,T 必须是元组,N 必须是大于等于 1 的整数。

1type Chunk<
2  T extends any[],
3  N extends number,
4  Result extends any[] = [],
5  Current extends any[] = []
6> = T extends [infer F, ...infer R] // 判断是否还有元素
7  ? Current['length'] extends N // 有元素,判断当前的块已经满了
8    ? Chunk<R, N, [...Result, Current], [F]> // 如果当前的块已经满了,把它放进结果数组里
9    : Chunk<R, N, Result, [...Current, F]> // 没有满,就把元素放进当前块
10  : Current['length'] extends 0 // T中所有元素都处理完了,判断当前块中是否有元素
11    ? Result // 当前块为空的,直接返回结果
12    : [...Result, Current]; // 否则把当前块放进结果,再返回
13

69. Medium - 4518 - Fill

Fill 是一个通用的 JavaScript 函数,现在来实现它的类型版本。Fill<T, N, Start?, End?> 接受 4 个参数, T 是一个元组,N 是任意类型, Start and End 是大于等于 0 的整数。把 T[Start.End] 范围内的元素都替换为 N

1type Fill<
2  T extends unknown[], // 原数组
3  N, // 要填充的类型
4  Start extends number = 0, // 开始下标
5  End extends number = T['length'], // 结束下标
6  Result extends unknown [] = [], // 结果数组
7  In extends boolean = false // 是否在[Start,End]范围内
8> = T extends [infer F, ...infer R] // T是否存在第一个元素
9  ? Result['length'] extends End // 先判断是否为结束下标
10    ? Fill<R, N, Start, End, [...Result, F], false>  // 是结束下标,则证明已经填充完了,后面填充T的内容就行
11    : Result['length'] extends Start // 不是,判断是否为开始下标
12      ? Fill<R, N, Start, End, [...Result, N], true>  // 是开始下标,则填充N,用IN=true表示已经在范围内
13      : In extends true // 判断是否在[Start,End]范围内
14        ? Fill<R, N, Start, End, [...Result, N], true> // 如果在范围内 则用N填充
15        : Fill<R, N, Start, End, [...Result, F], false> // 不在范围内 用T中内容填充
16  : Result // 处理完成,返回结果
17

70. Medium - 4803 - Trim Right

实现 TrimRight<T>,它接收确定的字符串类型并返回一个新的字符串,其中新返回的字符串删除了原字符串结尾的空白字符串。

1type TrimRight<S extends string> =
2  S extends `${infer F}${' '|'\n'|'\t'}` ? TrimRight<F> : S
3

71. Medium - 5117 - Without

实现一个像 Lodash.without 函数一样的泛型 Without<T, U>,它接收数组类型的 T 和数字或数组类型的 U 为参数,会返回一个去除 U 中元素的数组 T

Equal 是玄学,别问,用就完事了。

1type Includes<T extends readonly unknown[], U> = 
2  T extends [infer F, ...infer Rest]
3    ? Equal<F, U> extends true ? true : Includes<Rest, U>
4    : false;
5
6type Without<T extends any[], U extends any> = T extends [infer F, ...infer R]
7  ? U extends any[]
8    ? Includes<U, F> extends true // 如果U是数组类型,使用Includes判断是否包含
9      ? Without<R, U>
10      : [F, ...Without<R, U>]
11    : F extends U // 如果U不是数组,直接判断
12      ? Without<R, U>
13      : [F, ...Without<R, U>]
14  : []
15

评论区看到了更好的解法,先转成联合在判断是否包含

1type ToUnion<T extends any> = T extends any[] ? T[number] : T;
2
3type Without<T extends any[], U extends any> = 
4T extends [infer F, ...infer R]
5  ? F extends ToUnion<U>
6    ? Without<R, U>
7    : [F, ...Without<R, U>]
8  : [];
9

72. Medium - 5140 - Without

实现 Math.trunc 的类型版本,它接受字符串或数字,并通过删除所有小数来返回数字的整数部分。

简单的模板字符串模式匹配,注意 '-.3' 这种删除小数点后面的内容后需要手动补 0。

1type Trunc<T extends number | string> =
2  `${T}` extends `${infer F}.${infer _}`
3    ? F extends '-' | ''
4      ? `${F}0`
5      : F
6    : `${T}`;
7

73. Medium - 5153 - IndexOf

实现类型版本的 Array.indexOfindexOf<T, U> 接受两个参数,数组 T 和任意类型 U 返回 UT 中第一次出现的下标,不存在返回 -1

1type IndexOf<T extends any[], U, Pre extends any[] = []> =
2  T extends [infer F, ...infer R]
3    ? Equal<F, U> extends true
4      ? Pre['length']
5      : IndexOf<R, U, [...Pre, F]>
6    : -1
7

因为 TypeScript 无法进行计算,所以思路还是一样,用一个数组 Pre 记录已经遍历了几个数字,用 Pre['length'] 计数。

74. Medium - 5310 - Join

实现类型版本的 Array.joinJoin<T, U>接受一个数组 T 字符串或数字类型 U,返回 T 中的所有元素用 U 连接的字符串,U 默认为 ','

1type Join<
2  T extends any[],
3  U extends string | number = ',',
4  Pre extends string = ''
5> = T extends [infer F, ...infer R]
6  ? Pre extends ''
7    ? Join<R, U, `${F & string}`>
8    : Join<R, U, `${Pre}${U}${F & string}`>
9  : Pre;
10

其中 F & string 因为 F 的类型是 any 但是只有一部分类型可以反正模板字符串中,所以这里类型会把报错,通过 & string 限制为 string

75. Medium - 5317 - LastIndexOf

实现类型版本的 Array.lastIndexOf, LastIndexOf<T, U> 接受数组 T, any 类型的 U, 如果 U 存在于 T 中, 返回 U 在数组 T 中最后一个位置的索引, 不存在则返回 -1

1type LastIndexOf<
2  T extends any[],
3  U,
4  Pre extends any[] = [],
5  Result extends number = -1
6> = T extends [infer F, ...infer R]
7? Equal<F, U> extends true // 判断当前的值是否为U
8  ? LastIndexOf<R, U, [...Pre, F], Pre['length']> // 如果是,更新Result,然后继续处理
9  : LastIndexOf<R, U, [...Pre, F], Result>
10: Result
11

76. Medium - 5360 - Unique 数组去重

实现类型版本的 Lodash.uniq 方法,Unique 接收数组类型 T, 返回去重后的数组类型。

1type Includes<T extends readonly unknown[], U> = 
2  T extends [infer F, ...infer Rest]
3    ? Equal<F, U> extends true ? true : Includes<Rest, U>
4    : false;
5
6type Unique<
7  T extends any[],
8  Result extends any[] = [],
9> = T extends [infer F, ...infer R]
10  ? Includes<Result, F> extends true
11    ? Unique<R, Result>
12    : Unique<R, [...Result, F]>
13  : Result;
14

顺便评论区看到另一个,算是利用分布式条件类型更简洁的实现了 Includes

1type Unique<T, U = never> =
2  T extends [infer F, ...infer R]
3    ? true extends (U extends U ? Equal<U, [F]> : never)
4      ? Unique<R, U>
5      : [F, ...Unique<R, U | [F]>]
6    : []
7

77. Medium - 5821 - MapTypes 映射类型

实现 MapTypes<T, R>,把对象 T 中的类型根据 R 做转换。比如 R{ mapFrom: string, mapTo: boolean } 表示把 T 中的所有 string 类型改为 boolean

1// 根据类型映射的定义  原类型 获取映射后的类型
2type getType<T extends { mapFrom: any; mapTo: any }, P> = T extends any
3  ? T['mapFrom'] extends P // 利用分布式条件类型,依次判断是否匹配mapFrom类型
4    ? T['mapTo'] // 符合的返回对应的mapTo类型
5    : never // 不符合返回never 其他类型和never联合只会剩下其他类型
6  : never;
7
8type MapTypes<T, R extends { mapFrom: any; mapTo: any }> = {
9  // { mapFrom: T[K]; mapTo: any } 是否可以赋值给R
10  [K in keyof T]: { mapFrom: T[K]; mapTo: any } extends R
11    ? getType<R, T[K]> // 证明他的类型匹配了mapFrom 需要返回对应的mapTo
12    : T[K]; // 否则不需要调整
13};
14

78. Medium - 7544 - Construct Tuple 构造元组 ※

构造一个给定长度的元组。

这题简直太水了,递归就可以 TypeScript 最多递归到999层,所以最后一个 case Expect<Equal<ConstructTuple<1000>['length'], 1000>> 会失败。

1// 生成元组,但是TS递归只能到999
2type ConstructTuple<
3  N extends string | number,
4  Result extends any[] = []
5> = `${Result['length']}` extends `${N}`
6  ? Result
7  : ConstructTuple<N, [...Result, unknown]>;
8

但是,让 9999 成功才是有趣的问题,我开始一直想二分,把自己困住了,后来发现简单的按位计算就可以。参考github.com/type-challe…

1// 生成元组,但是TS递归只能到999
2type ConstructTupleSimple<
3  N extends string | number,
4  Result extends any[] = []
5> = `${Result['length']}` extends `${N}`
6  ? Result
7  : ConstructTupleSimple<N, [...Result, unknown]>;
8
9// 把数组T中的元素数量*10
10type Multi10<T extends any[]> = [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T]
11
12// 从左到右依次计算 例321 = (3*10+2)*10+1
13type ConstructTuple<
14  L extends number | string,
15  Result extends any[] = []
16> = `${L}` extends `${infer F}${infer R}`
17  ? ConstructTuple<R, [...Multi10<Result>, ...ConstructTupleSimple<F>]>
18  : Result;
19

79. Medium - 8640 - Number Range

构造指定范围内所有数字的联合。

好像在前面做过类似的题目……通过 Arr 辅助记录遍历数字,Result 记录结果,InRange 记录是否在范围内。

1type NumberRange<L, H, Arr extends any[] = [], Result = never, InRange = false> =
2  Arr['length'] extends L
3    ? NumberRange<L, H, [...Arr, unknown], L | Result, true>
4    : Arr['length'] extends H
5      ? Result | H
6      : InRange extends true
7        ? NumberRange<L, H, [...Arr, unknown], Arr['length'] | Result, InRange>
8        : NumberRange<L, H, [...Arr, unknown], Result, InRange>
9

也有更直观的解法:

1type ConstructUnion<
2  N extends string | number,
3  Result extends any[] = []
4> = `${Result['length']}` extends `${N}`
5  ? Result[number]
6  : ConstructUnion<N, [...Result, Result['length']]>;
7
8type NumberRange<L extends number, H extends number> =
9  | Exclude<ConstructUnion<H>, ConstructUnion<L>>
10  | L
11  | H;
12

80. Medium - 8767 - Combination ※

给定一个字符串数组,执行排列和组合。

1// 计算T中每个元素开头的 T和P中所有元素组成的全部组合
2type Combination<T extends string[], P extends string[] = []> =
3  T extends [infer F extends string, ...infer R extends string[]] // 先计算F开头的所有情况
4    ? `${F} ${Combination<[...R, ...P]>}` // 首个单词是F 然后连接 其他单词的全排列
5        //(在模板字符串中的联合类型会自动生成所有情况的模板字符串结果的联合)
6      | Combination<R, [...P, F]> // 继续计算单个单词剩余单词的情况
7      | F // 单个单词
8    : never
9

这种题我每次都要花一个小时做出来,头痛。看了下别人的解法很nb,利用联合类型分布式遍历 T,少了一次递归。

1type Combination<T extends string[], All = T[number], Item = All>
2  = Item extends string
3    ? Item | `${Item} ${Combination<[], Exclude<All, Item>>}`
4    : never
5

81. Medium - 8987 - Subsequence ※

给定一个唯一元素数组,返回所有可能的子序列。

1type UnionAddT<U extends any[], T> = U extends any ? [T, ...U] : never
2type Subsequence<T extends any[]> = T extends [infer F, ...infer R]
3  ? Subsequence<R> // 不包含F的所有子序列
4      | UnionAddT<Subsequence<R>, F> // 包含F的所有子序列
5  : []
6

这题不难,加了重点标识因为新学了一个语法:... 展开运算符应用到联合类型时,会对联合类型的每个成员分别展开,然后将结果再组成联合类型。

1type Subsequence<T extends unknown[]> = T extends [infer X, ...infer Y]
2  ? [X, ...Subsequence<Y>] | Subsequence<Y>
3  : [];
4

82. Medium - 9142 - CheckRepeatedChars

实现类型 CheckRepeatedChars<S> 返回 S 中是否有重复字符。

1type CheckRepeatedChars<
2  T extends string,
3  visited = never
4> = T extends `${infer F}${infer R}`
5  ? F extends visited
6    ? true
7    : CheckRepeatedChars<R, visited | F>
8  : false;
9

83. Medium - 9286 - FirstUniqueCharIndex

给一个字符串 S 找到第一个不重复字符的下标,不存在返回 -1。 (灵感来自 leetcode 387)(笑死力扣都来了)

1type GetRepectChars<T extends string, Once = never, Repeated = never> = 
2  T extends `${infer F}${infer R}`
3    ? F extends Once
4      ? GetRepectChars<R, Once, Repeated | F>
5      : GetRepectChars<R, Once | F, Repeated>
6    : Repeated
7
8type FirstUniqueCharIndex<T extends string, Repeated = GetRepectChars<T>, Index extends any[] = []> = 
9  T extends `${infer F}${infer R}`
10    ? F extends Repeated
11      ? FirstUniqueCharIndex<R, Repeated, [...Index, F]>
12      : Index['length']
13    : -1
14

84. Medium - 9616 - Parse URL Params

实现类型层面的解析器,把 URL 中的参数字符串解析为一个联合。

1type ParseUrlParams<T extends string> = T extends `${infer F}/${infer R}`
2  ? F extends `:${infer P}`
3    ? P | ParseUrlParams<R>
4    : ParseUrlParams<R>
5  : T extends `:${infer P}`
6    ? P
7    : never
8

85. Medium - 9896 - GetMiddleElement

通过实现一个 GetMiddleElement 方法,获取数组的中间元素,用数组表示

如果数组的长度为奇数,则返回中间一个元素 如果数组的长度为偶数,则返回中间两个元素

1type GetMiddleElement<T extends any[]> =
2  T['length'] extends 0 | 1 | 2
3    ? T
4    : T extends [infer _L, ...infer M, infer _R]
5      ? GetMiddleElement<M>
6      : []
7

简单,每次删除前后两个元素,对长度为 0 1 2 的数组特殊处理。

86. Medium - 9898 - Appear only once

找出目标数组中只出现过一次的元素。例如:输入 [1,2,2,3,3,4,5,6,6,6],输出 [1,4,5]

1// 判断联合类型T中是否存在U
2type Includes<T, U> = true extends (T extends any ? Equal<T, U> : never)
3  ? true
4  : false;
5
6type FindEles<
7  T extends any[],
8  Pre extends any[] = [],
9  Res extends any[] = []
10> = T extends [infer F, ...infer R]
11  ? Includes<[...Pre, ...R][number], F> extends true // 如果F前后组成的数组是否包含F
12    ? FindEles<R, [...Pre, F], Res> // 包含F 则证明不唯一 结果不添加F
13    : FindEles<R, [...Pre, F], [...Res, F]> // 不包含F 则证明唯一 结果添加F
14  : Res; // 遍历结束 返回结果
15

87. Medium - 9989 - Count Element Number To Object

通过实现一个 CountElementNumberToObject 方法,统计数组中相同元素的个数。

1// 把数组拍平,然后把其中never元素删除
2type Flatten<A extends any[]> = A extends [infer F, ...infer R] // 判断A存在第一个元素F
3  ? [F] extends [never]
4    ? [...Flatten<R>]
5    : F extends any[]
6    ? [...Flatten<F>, ...Flatten<R>]
7    : [F, ...Flatten<R>]
8  : [];
9
10type CountElementNumberToObject<
11  T extends any[],
12  // 辅助计数的对象,用数组计数
13  Aux extends Record<string | number, any[]> = {},
14  // T中有嵌套数组 把T拍平
15  F extends (number | string)[] = Flatten<T>
16> = F extends [
17  infer L extends number | string, // 取第一个元素
18  ...infer R extends (number | string)[]
19]
20  ? CountElementNumberToObject<
21      R,
22      {
23        [K in keyof Aux | L]: K extends L // 遍历Aux中key
24          ? L extends keyof Aux // 遍历到L了,判断如果L是Aux中的key
25            ? [...Aux[K], unknown] // 就在对应数组中添加一个元素
26            : [unknown] // 不在就新创建一个数组,添加一个元素
27          : Aux[K]; // 其他key不做处理
28      }
29    >
30  : {
31      [K in keyof Aux]: Aux[K]['length']; // 把结果映射为数组的长度
32    };
33

88. Medium - 10969 - Integer ※

请完成类型 Integer<T>,类型 T 继承于 number,如果 T 是一个整数则返回它,否则返回 never

1type OnlyZero<T> = T extends `${infer F}${infer R}`
2  ? F extends '0'
3    ? OnlyZero<R>
4    : false
5  : true;
6type ToNumber<T> = T extends `${infer N extends number}` ? N : never;
7type Integer<T> = T extends number
8  ? number extends T
9    ? never
10    : `${T}` extends `${infer Int}.${infer Deci}`
11    ? OnlyZero<Deci> extends true
12      ? ToNumber<Int>
13      : never
14    : T
15  : never;
16

虽然也不算难,但是一看评论区天塌了。

1type Integer<T extends string | number> = number extends T
2  ? never
3  : `${T}` extends `${string}.${string}`
4  ? never
5  : T;
6// 或者这样,因为 bigint 只能是整数
7type Integer<T extends number> = `${T}` extends `${bigint}` ? T : never
8

我自己试了一下数字转字符串,发现对于多余的小数点后面的 0 会被删除。

1type x = `${1.0}` // "1"
2type x1 = `${1.2}` // "1.2"
3type x2 = `${1.200}` // "1.2"
4

89. Medium - 16259 - ToPrimitive ※

把对象中类型为字面类型(标签类型)的属性,转换为基本类型。

这题可以枚举类型实现,不过就没啥意思了。看到一种神奇的解法,用到了 [valueOf](function valueOf() { [native code] })。

1type ToPrimitive<T> = T extends (...args: any[]) => any
2  ? Function
3  : T extends object
4    ? { [K in keyof T]: ToPrimitive<T[K]> }
5    : T extends { valueOf: () => infer R }
6      ? R
7      : T;
8

JavaScript 中每个包装对象都有 [valueOf()](function valueOf() { [native code] }) 方法:

  • [String.prototype.valueOf()](function valueOf() { [native code] }) 返回 string
  • [Number.prototype.valueOf()](function valueOf() { [native code] }) 返回 number
  • ...

在 TypeScript 类型系统中,我们可以利用这个特性:

1interface String {
2  /** Returns the primitive value of the specified object. */
3  valueOf(): string;
4  // ... 其他方法
5}
6
7// string 字面量类型
8type Test1 = 'Tom' extends { valueOf: () => infer R } ? R : never
9// 'Tom'  string 类型,string  valueOf(): string
10// R = string 
11
12type Test2 = 30 extends { valueOf: () => infer R } ? R : never
13// 30  number 类型,number  valueOf(): number
14// R = number 
15

90. Medium - 17973 - DeepMutable

实现一个通用的 DeepMutable ,它使对象的每个属性,及其递归的子属性 - 可变。

1type DeepMutable<T extends object> = {
2  -readonly [K in keyof T]: T[K] extends (...args: any) => any 
3    ? T[K]
4    : T[K] extends object ? DeepMutable<T[K]> : T[K]
5}
6

TypeScript 类型体操练习笔记(二)》 是转载文章,点击查看原文


相关推荐


【Kubernetes专项】K8s 配置管理中心 ConfigMap 实现微服务配置管理
.Kaser.2026/2/9

十六、K8s 配置管理中心 ConfigMap 实现微服务配置管理 16.1 ConfigMap 相关概念及cm字段 16.1.1 ConfigMap 概述 ​ Configmap 是 k8s 中的资源对象,用于保存非机密性的配置的,数据可以用 key/value键值对 的形式保存,也可通过 文件 的形式保存。 Configmap 是 k8s 中的资源, 相当于配置文件,可以有一个或者多个 Configmap;Configmap 可以做成 Volume,k8s pod 启动之后,通过 volu


VScode引入claude+deepseek
何亚告2026/1/31

最近由于项目需求以及效率需要,在vscode引入claude进行代码整理,现将引入过程记录,将相关踩坑问题复盘: 1. 安装CC-Switch ccSwitch(CC-Switch)是基于 Rust+Tauri 开发的跨平台桌面应用,核心作用是一键管理与切换 Claude Code、Codex、Gemini CLI 等 AI 编程工具的 API 配置,替代手动修改 JSON / 环境变量,大幅提升配置效率。以下是核心功能与价值 安装包下载地址:https://github.com


多标签页强提醒不重复打扰:从“弹框轰炸”到“共享待处理队列”的实战
_Jude2026/1/22

场景:我在多标签页里“接力”处理紧急待办 这篇文章讨论的不是“消息列表怎么做”,而是紧急待办的强提醒体验应该如何落地。我的核心需求很明确: 紧急消息必须强制弹框提醒(不能靠用户自己去小铃铛里找) 弹框不能手动关闭,只能通过“去处理/已读”等业务动作逐条消解 刷新后仍要继续弹:只要还有“高优先级且未处理”的消息,就必须再次弹框 多标签页不重复打扰:同一时间只允许一个标签页弹;未处理的消息能跨标签页接力,不丢失 ✅ 问题 1:多标签页重复强弹(“弹框轰炸”)💥 现象 A 中点“去处理”打开


10分钟复刻爆火「死了么」App:vibe coding 实战(Expo+Supabase+MCP)
mCell2026/1/14

视频链接:10分钟复刻爆火「死了么」App:vibe coding 实战 仓库地址:github.com/minorcell/s… 最近“死了么”App 突然爆火:内容极简——签到 + 把紧急联系人邮箱填进去。 它的产品形态很轻,但闭环很完整: 你每天打卡即可;如果你连续两天没打,系统就给紧急联系人发邮件。 恰好我最近在做 Supabase 相关调研,就顺手把它当成一次“极限验证”: 我想看看:Expo + Supabase 能不能把后端彻底“抹掉” 我也想看看:Codex + MCP 能


耗时 8 天,我用 Claude Code 开发了 AI 漫剧 APP,并开源了。
苍何2026/1/5

这是苍何的第 468 篇原创! 大家好,我是热爱编程的苍何。 去年底的时候,我写过 2 篇 AI 漫剧的文章,感兴趣的还挺多的。 也认识了非常多做 AI 漫剧的朋友,我们武汉 AI 圈也举办了 AI 漫剧沙龙,来了超级多的感兴趣的圈友。 听了很多的干货分享,当时脑海中只想快速上手来做漫剧。 但我看了很多的平台目前还只能在电脑 web 上操作,手机随时创作我还没找到什么好的 APP。 当时就有一股冲动,要不自己来尝试搞一个?当我和老婆说这个想法的时候,她说我一定疯了。 为了证明我不是疯子,我还


数据结构(四)————图
旺仔小拳头..2025/12/27

1. 无向图与有向图 1.1 定义 无向图:边是无方向的,用(顶点, 顶点)表示边有向图:边(称为 “弧”)是有方向的,用<弧尾, 弧头>表示方向 2. 连通图 2.1 连通的定义 在无向图中,若从顶点v到顶点w存在路径,则称v到w是连通的。 2.2 连通图的定义 若图中任意两个顶点都连通,则称此图为连通图。 3. 完全图 3.1 定义 具有最多边数的图称为完全图。 3.2 边数公式 无向完全图(n 个顶点):边数最大值为n(n-1)/2。有向完全图(n 个顶点):边数最


OpenAI 甩出王炸:GPT-5.2-Codex 上线,这次它想做你的“赛博合伙人”
墨风如雪2025/12/19

老实说,在 AI 模型像下饺子一样发布的 2025 年年底,大家对“颠覆性升级”这个词早就脱敏了。但 OpenAI 刚刚在 12 月 18 日悄悄放出的 GPT-5.2-Codex,还是让不少熬夜写代码的工程师虎躯一震。 这不仅仅是 GPT-5.2 的一个微调版本,更像是一次针对程序员痛点的“精准爆破”。如果说以前的 AI 是帮你补全代码的实习生,那么这次上线的 Codex,更像是一个能扛事儿的“高级合伙人”。 我花了一点时间扒了扒这背后的技术细节和实测数据,有些东西确实值得聊聊。 告别“金鱼


Cursor 又偷偷更新,这个功能太实用:Visual Editor for Cursor Browser
张拭心2025/12/11

凌晨 1 点,我正要关电脑睡觉,屏幕左下角突然弹出一个弹窗: Cursor 又上新功能了?带着好奇我仔细看了下文档:cursor.com/cn/docs/age… 我去,这个功能很重磅啊! 这次更新的 Visual Editor for Cursor Browser 是一个打破“设计”与“编码”边界的重磅功能,它让 Cursor 不仅仅是编辑器,更是一个“能直接写代码的浏览器”。 核心价值 它解决了前端开发中最大的痛点——“在浏览器里调好了样式,还得手动回代码里改”。 现在,我们可以像在 Fi


桌面应用开发,Flutter 与 Electron如何选
Karl_wei2025/12/1

前言:这一年来我基本处于断更的状态,我知道在AI时代,编码的成本已经变得越来越低,技术分享的流量必然会下降。但这依然是一个艰难的过程,日常斥责自己没有成长,没有作品。 除了流量问题、巨量的工作,更多的原因是由于技术栈的变化。我开始使用Electron编写一个重要的AI产品,并且在 Flutter 与 Electron 之间来回拉扯...... 背景 我们对 Flutter 技术的应用,不仅是在移动端APP,在我们的终端设备也用来做 OS 应用,跨Android、Windows、Linux系统。


AI 原生应用开源开发者沙龙·深圳站精彩回顾 & PPT下载
阿里云云原生2026/2/26

作者:盈楹 近日,AI 原生应用开源开发者沙龙·深圳站圆满落幕。本场活动吸引了 140+ 名技术从业者深度参与,聚焦 AI 原生应用架构领域的开源技术与落地实践, 围绕 AgentScope、RocketMQ、HiMarket、Higress、LoongSuite、Agent 技术实践等议题展开深度分享,并设置了动手实操环节。 关注「阿里云云原生」公众号,后台回复:0210 免费获得深圳站讲师 PPT 合辑 精彩回顾 议题一:AgentScope:迈向 Agentic 智能体应用丨高大伟(大玮)

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客