type challenge刷题之(middle 部分)示例解析
作者:goblin_pitche
这篇文章主要为大家介绍了type challenge刷题之(middle 部分)示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
引言
续接上篇:[easy 部分] https://www.jb51.net/javascript/295625e3z.htm
熟悉了基本知识点之后,middle整体比较顺畅,就是题目太多,更多是知识点的查漏补缺。
middle部分
type MyReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : never; // 实现Omit type Exclude<T, K> = T extends K ? never : T; type MyOmit<T, K extends keyof T> = {[k in Exclude<keyof T, K>]: T[k]} // 指定部分readonly,此处假设已经实现了MyOmit.. type MyReadonly2<T, K extends keyof T = keyof T> = {readonly [k in K]: T[k]} & MyOmit<T, K> type DeepReadonly<T> = { readonly [P in keyof T]: keyof T[P] extends never ? T[P] : DeepReadonly<T[P]>; }; type TupleToUnion<T extends any[]> = T[number]; type Last<T extends any[]> = T extends [...any[], infer U] ? U : never; type Pop<T extends any[]> = T extends [...infer U, any] ? U : T; type LookUp<U, T> = U extends {type: T} ? U : never; // Trim相关,先实现Space类型 type Space = ' '|'\n'|'\t'; type TrimLeft<S extends string> = S extends `${Space}${infer U}` ? TrimLeft<U> : S; type Trim<S extends string> = S extends `${Space}${infer U}`|`${infer U}${Space}` ? Trim<U>: S; type Replace<S extends string, From extends string, To extends string> = From extends '' ? S : (S extends `${infer H}${From}${infer T}` ? `${H}${To}${T}` : S);
DeepReadonly
这块内容还是【分配条件类型】没掌握好,可以参看另一篇文档.
Chinable
type MyAssign<T extends {}, Q extends {}> = {[k in (keyof T|keyof Q)]: k extends keyof Q ? Q[k] : (k extends keyof T ? T[k]: undefined)} type Chainable<T extends {} = {}> = { option<P extends string, Q>(key: P extends keyof T ? never : P, value: Q): Chainable<MyAssign<T, Record<P, Q>>> get(): T }
这里难点有两个:
重复赋值时,怎么让其报错
const result2 = a .option('name', 'another name') // @ts-expect-error .option('name', 'last name') .get()
- 给
name
属性赋值后,再次赋值name
属性就会报错,但ts type
没法直接用非判断,大多只能利用其补集进行判断 - 参数侧
option<P extends string, Q>
肯定是没法用条件语句进行判断,如果这么写option<P extends (P extends keyof T ? never : P), Q>
,会直接报错<span style="color: red">Type parameter 'P' has a circular constraint.</span> - 因此,只能在函数的参数中进行判定,
option<P extends string, Q>(key: P extends keyof T ? never : P, value: Q)
新添加的属性,需要实现类似
Assign
的方法- 这里不能用
Object.assign
方法的实现方式,原本Object.assign
的实现方式如下 assign<T extends {}, U>(target: T, source: U): T & U;
- 这种实现方式带来了一个问题,即
const b = Object.assign(aa, { name: 11 });
,typeof b
的结果为{ name: string;} & { name: number;}
,显然不符合需求,因此自己实现 type MyAssign<T extends {}, Q extends {}> = {[k in (keyof T|keyof Q)]: k extends keyof Q ? Q[k] : (k extends keyof T ? T[k]: undefined)}
- 这里不能用
PromiseAll
// 最终答案 declare function PromiseAll<A extends any[]>(values: readonly [...A]): Promise<{ [key in keyof A]: Awaited<A[key]> }>
这一题如果按照测试用例慢慢拼的话,也不算太难,最开始拼凑出来的写法如下:
type PromiseValue<T> = T extends Promise<infer V> ? PromiseValue<V> : T; type PromiseValues<T extends any[], R extends any[] = []> = T extends [infer H, ...infer E] ? PromiseValues<E, [...R, PromiseValue<H>]>: ([] extends R ? PromiseValue<T[number]>[] : R); declare function PromiseAll<T extends any[]>(values: readonly [...T]): Promise<PromiseValues<T>>;
这里遇到了两个问题:
PromiseAll<T extends any[]>(values: readonly [...T])
这里为什么要用readonly- 其中有个测试用例是这样的
PromiseAll([1, 2, Promise.resolve(3)] as const)
- 自己没查到
as const
是什么意思,问了chat-gpt,给的答复是这里的as const
是将数据变为只读,这样的话很明确了,readonly
的T
不是T
的子类,我们要将values
提升为readonly
的类型
- 其中有个测试用例是这样的
是否可以用对象表示数组
type A = [number, string, {a: number}] type B = { [k in keyof A]: A[k] } type C = A extends B ? (B extends A ? true: false): false; // true!!!
- 这种复制对象的方式,可以用在数组上
MyCapitalize
这一题一开始没想多少,直接写出了正确答案
type MyCapitalize<S extends string> = S extends `${infer H}${infer T}` ? `${Uppercase<H>}${T}` : S;
但回头想到了一个问题,infer H
为什么只匹配了第一个字母,正则具有贪婪性,infer
不具备吗?
网上没找到相关文章,问了chat-gpt,它一本正经告诉我infer H
这种格式的语法只能匹配一个字母,并给了我下面的例子
type Substring2<S extends string> = S extends `${infer A}${infer _}${infer B}` ? `${A}${B}` : never; type Test = Substring2<'hello'>; // chat-gpt说结果是'hl',实际跑是'hllo'
多测试了几个例子,诸如
type UpperFirstPart<S extends string> = S extends `${infer H} ${infer T}` ? `${Uppercase<H>} ${T}` : S; type Test = UpperFirstPart<"hello world">; // HELLO world
发现infer
会以第一个找到的匹配模式为标准,类似Array.find
方法,这样的话,理解MyCapitalize
的实现就容易多了
以上就是type challenge刷题之(middle 部分)示例解析的详细内容,更多关于type challenge middle的资料请关注脚本之家其它相关文章!