TypeScript Type Chanllenges
TypeScript Type Chanllenges
Information
Raise hands
- Tupple To Union
- Permutation
- Permutation
- A|B to [A,B]
- last one easy version
- A|B to A&B
- EveryIsNum
- Another Permutation
Warm Up
Hello Wolrd
Easy
Pick
Readonly
Tuple to Object
First of Array
Length of Array
Exclude
Awaited
If
Concat
Includes
Push
Unshift
Parameters
Medium
Get Return Type
Omit
Readonly2
Deep Readonly
Tuple to Union
Chainable Options
Last of Array
Pop
PromiseAll
Type LookUp
Trim Left
Trim
Capitalize
Replace
ReplaceAll
Append Argument
Permutation
新人杀手 & 老人杀手。总结,杀麻了。
Length of String
Flatten
Append to Object
Absolute
String to Union
Merge
CamelCase
KebabCase
Diff
AnyOf
IsNever
IsUnion
如何判断传入的类型是联合类型?这个 Issue 详细讨论了代码原理:IsUnion。
type IsUnionImpl<T, C extends T = T> = (T extends T ? C extends T ? true : unknown : never) extends true ? false : true;
type IsUnion<T> = IsUnionImpl<T>;
主要逻辑梳理如下:
- 使用 T extends T ? : 使裸类型 T 参与运算使得 C extends T 时有能力进行扩展。
- 如果是单类型,C extends T 一直为 true,所以最终返回 false。
- 如果是联合类型,由于 C 和 T 都会展开,所以展开再运算后会得到一个类似 true | unknown | ... | ... 的结果,所以最终返回 true。
在网上找到个更短的实现,原理稍微有点不同:
- 使用 T extends ... 的形式使后续运算有能力展开 T。
- 所以 U extends T 中的 T 变成了 string、number 这种单类型元素元组。
- 如果 T 是单类型,U extends T 为真,最终返回 false。
- 如果 T 是联合类型,U extends T 为假,最终返回 true。
type IsUnion<T, U = T> = T extends any ? [U] extends [T] ? false : true : never
在这题花了好多时间,感觉对 Union 类型的操作不是很熟练。Type Chanllenges 里面的题目也很少有 Union 相关的。哦,至少这之前没有要直接操作 Union 的题目。
Replace Keys
Remove Index Signature
Percentage Parser
Drop Char
Minus One
关于蹦床的解答见:Minus One
常见思路是每次递归不要用加法,而是用乘法。见 Minus One。它能支持计算 9999,但在超过 9999 时会报错。这个限制是死的,没有办法优化。
蹦床理论上可以无限计算下去。额,好吧,在等待了接近 10 秒钟后也报错了。就算优化递归调用的层数也很难解决这个问题:TS Playground。extends 的层级嵌套带来的性能下降是指数级的,测试在一个函数嵌套 15 个 extends 就会使它变得非常满,若超过 25 个,编译器会一直处于卡死状态;函数的嵌套也是有限的。以上代码展示了嵌套 15 层 extends 的函数再嵌套带来的改进,它能计算到 3333 左右大小的数字。见 test6 测试项 4444,编译器放弃了计算,直接返回了 any。
PickByType
Starts With
Ends With
Partial By Keys
Required By Keys
Mutable
OmitByValue
ObjectEntries
这题有难度,大部分人的答案都过不了测试。
Shift
Tuple to Nested Object
Reverse
Flip Arguments
FlattenDepth
写好了才发现递归过不了测试,呜呼,麻了,TSPlayGround。要想不用递归的话,思路是累加。
BEM style string
Inorder Traversal
TODO: https://github.com/type-challenges/type-challenges/issues/6278
type InorderTraversal<T extends TreeNode | null> = T extends TreeNode
? [...InorderTraversal<T["left"]>, T["val"], ...InorderTraversal<T["right"]>]
: []
type InorderTraversal<T extends TreeNode | null> = [T] extends [TreeNode]
? [...InorderTraversal<T['left']>, T['val'], ...InorderTraversal<T['right']>]
: [];
Flip Object
Fibonacci
两种解法,花了一晚上。全排列有许多种解法,但是细节很难控制,主要是这题要求字母不能重复,所以只能一个个取出来再和剩下的全排列组合。最难的地方是怎么把联合类型一个个取出来。
- 1:{ P in Union: "${P} ${Permutation<Exclude<Union, P>>}" }Union
- 2:Combine<UA, UB> = UA | UB | "UA UB" | "UB UA"
Greater Than
Zip
IsTupple
Using number extends T'length' or T'length' extends number,thats a problem。
Chunk
Fill
Trim Right
Without
Trunc
IndexOf
Join
LastIndexOf
Unique
要是不知道 IfEqual 的更完善版本的写法,估计这题会是杀手级别的题目。
MapTypes
又是集合。如和从匹配到多个集合中选出特定的那个,有时可以用 infer,比如上面某题目用 (infer R | undefined) 去除了 undefined。这题通过 { key1: R[Key1], key: infer X } 的形式推断出了具体的 X。
Object Key Paths
I'm confused.
Construct Tupple
Number Range
[Permutation & Combination()
集合相关难到爆炸。但是题目也许有问题,待讨论:Discussion: Permutation & Combination
type UnionToIntersection<U> =
(U extends U ? ((k: (x: U) => void) => void) : never) extends ((k: infer I) => void)
? I
: never
type UnionLast<U> = UnionToIntersection<U> extends ((x: infer R) => void) ? R : never
Subsequence
Hard
Simple Vue
Curring
Union to Intersection
Get Required
keyof Partial<T> 拿到的居然是不带 Required 标志的 key!所以判断属性的键的 Required 是否一致,需要构造一个新对象,来对比相同的键是否能从中获得相同的值。额,不能用 Partial<T> 去构造新对象,只能用 Required,很迷。
type GetRequired<T> = {
[K in Exclude<keyof T, keyof Partial<T>>]: T[K]
}
Get Optional
Get Required
Optional Keys
Capitalize Word
CamelCase
C Printf
Vue Basic Props
SKIP,TODO
IsAny
Typed Get
String to Number
Tuple Filter Out
Tuple to Enum Object
TODO,跳过,不想碰 Enum
Printf
Deep Object To Unique
Length Of String 2
Union to Tupple
Deep Pick
Piana
TODO
Camelize
Drop String
Split
Class Public Keys
IsRequiredKey
Object From Entries
在 Issue 意外发现一种可以收敛集合的方法。
type ObjectFromEntries<T extends [string, unknown]> = {
[K in T[0]]: T extends [ K, unknown ] ? T[1] : never
}
IsPalindrome
Mutable Keys
出现了,出现了!IfEquals 出现了!
Intersection
数组和字符串都有快速转 Union 的方法,不要傻傻写 MakeUnion 了,Arr[number]。
Binary To Decimal
Two Sum
Extreme
Get Readonly Keys
Query String Parser
TODO pass
Slice
Integers Comparator
Curring 2
这个递归太美妙了。
type Curry<Args, Return, PreArgs extends unknown[] = []> =
Args extends [infer One, ...infer Rest]
? Rest extends []
? (...x: [...PreArgs, One]) => Return
: ((...x: [...PreArgs, One]) => Curry<Rest, Return>) & Curry<Rest, Return, [...PreArgs, One]>
: never
declare function DynamicParamsCurrying<A extends unknown[], R>(fn: (...args: A) => R): Curry<A, R>
type test = Curry<['a', 'b', 'c'], true>
((x_0: "a") => ((x_0: "b") => (x_0: "c") => true) & ((x_0: "b", x_1: "c") => true)) &
((x_0: "a", x_1: "b") => (x_0: "c") => true) & ((x_0: "a", x_1: "b", x_2: "c") => true);
BigInt Sum
看到两个很赞的思路:
- 用矩阵提前缓存两数相加的结果。
- 用 Arr extends [1,2,3...10,...infer R] 来获取 R 的长度。
Multiply
TODO, SKIP
Tag
TODO, SKIP
Inclusive Range
TODO,这个好像是每次递增 5 来减小递归次数的思路:https://github.com/type-challenges/type-challenges/issues/8055。