Ts

TypeScript Type Chanllenges

TypeScript Type Chanllenges

Information

Home Page

Raise hands

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 变成了 stringnumber 这种单类型元素元组。
  • 如果 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 时会报错。这个限制是死的,没有办法优化。

Type produces a tuple type that is too large to represent.

蹦床理论上可以无限计算下去。额,好吧,在等待了接近 10 秒钟后也报错了。就算优化递归调用的层数也很难解决这个问题:TS Playground。extends 的层级嵌套带来的性能下降是指数级的,测试在一个函数嵌套 15 个 extends 就会使它变得非常满,若超过 25 个,编译器会一直处于卡死状态;函数的嵌套也是有限的。以上代码展示了嵌套 15 层 extends 的函数再嵌套带来的改进,它能计算到 3333 左右大小的数字。见 test6 测试项 4444,编译器放弃了计算,直接返回了 any。

Type instantiation is excessively deep and possibly infinite.

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。

Sort
Distribution Unions
Assert Array Index
JSON Parser
Subtract

Copyright © 2024 Lionad - CC-BY-NC-CD-4.0