TypeScript训练
# 说在前面
训练题目来源 ts训练营 (opens new window)
关于标记和题目难易度说明
🚀 表示地狱级别
🍅 表示是难题
🚪 表示简单级别题目
Ⓜ️ 表示中等题目
❌ 表示题目未处理
🤔 表示题目值得思考
❓ 表示还有疑问
🍵 表示题目值得放松一下的简单题
其他符号没什么含义 ?嗯... 【我肯定是这样认为的】
题目难易度不一定是从简单到复杂的,有些题目放在一起是为了方便归类。
# 简单系列
实现 Pick
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyPick<Todo, 'title' | 'completed'>
const todo: TodoPreview = {
title: 'Clean room',
completed: false,
}
2
3
4
5
6
7
8
9
10
11
12
提示
- in
- keyof
- extends
点击查看答案
type MyPick<T, K extends keyof T> = {
[key in K]: T[key]
}
2
3
4
5
# 🚪 实现元组转换对象
传入一个元组类型,将这个元组类型转换为对象类型,这个对象类型的键/值都是从元组中遍历出来。
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const
type result = TupleToObject<typeof tuple> // expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}
2
3
错误示例
点击查看
type TupleToObject<T extends any[]> = {
[K in T[number]]: K
}
2
3
正确答案
点击查看答案
type TupleToObject<T extends (number | symbol | string)[]> = {
[K in T[number]]: K
}
2
3
# 🚪 实现 exclude
提示
- never
- extends
点击查看答案
type MyExclude<T, U> = U extends T ? never : T
# 🚪 实现 Include
提示
- never
- extends
点击查看答案
type MyInclude<T, U> = U extends T ? T : never
# 🚪 实现 If
实现一个 IF
类型,它接收一个条件类型 C
,一个判断为真时的返回类型 T
,以及一个判断为假时的返回类型 F
。 C
只能是 true
或者 false
, T
和 F
可以是任意类型。
example
type A = If<true, 'a', 'b'> // expected to be 'a'
type B = If<false, 'a', 'b'> // expected to be 'b'
2
提示
- boolean
- extends
点击查看答案
type If<T extends boolean, U, P> = T extends true ? U : P
# 数组系列
# 🚪 实现 First
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
//
type head1 = First<arr1> // expected to be 'a'
type head2 = First<arr2> // expected to be 3
2
3
4
5
6
点击查看答案
type First<T extends any[]> = T extends [infer F, ...infer] ? F : []
# 🚪 实现 End
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
//
type head1 = End<arr1> // expected to be 'c'
type head2 = End<arr2> // expected to be 1
2
3
4
5
6
点击查看答案
type End<T extends any[]> = T extends [...infer, infer E] ? E : []
# 🚪 实现 Unshitf
type Result = Unshift<[1, 2], 0> // [0, 1, 2]
2
3
点击查看答案
type Unshift<T extends any[], U> = [U, ...T]
# 🚪 实现 Shitf
type Result = Shift<[1, 2, 2, 3]> // [2,3,3]
2
点击查看答案
type Shift<T extends any[]> = T extends [infer L,...infer R]?R:T
# 🚪 实现 Push
type Result = Push<[1, 2], 0> // [1,2,0]
2
3
点击查看答案
type Push<T extends any[], U> = [...T, U]
# 🚪 获取 Length
创建一个通用的Length
,接受一个readonly
的数组,返回这个数组的长度。
type tesla = ['tesla', 'model 3', 'model X', 'model Y']
type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT']
type teslaLength = Length<tesla> // expected 4
type spaceXLength = Length<spaceX> // expected 5
2
3
4
5
提示
- extends
- ts中获取数组获取长度的方式
点击查看答案
type Length<T extends readonly any[]> = T['length']
# 🚪 实现 concat
提示
数组的解构赋值
点击查看答案
type Concat<T extends readonly unknown[], U extends readonly unknown[]> = [...T, ...U];
# 🚪 实现 Reverse
type A = Reverse<[1, 2, 3, 4]> // [4, 3, 2, 1]
构造一个数组存放反转数组
点击查看答案
// 解法一
type Reverse<T extends any[], U extends any[] = []> = T extends [...infer L, infer R] ? Reverse<L, [...U, R]> : U
// 解法二
type Reverse<T extends any[]> = T extends [...infer F, infer R] ? [R, ...Reverse<F>] : T
2
3
4
5
# Ⓜ️ 实现 LastIndexOf
实现Array.lastIndexOf的类型版本,lastIndexOf<T,U>获取数组T,任意U,并返回数组T中最后一个U的索引
type Res1 = LastIndexOf<[1, 2, 3, 2, 1], 2> // 3
type Res2 = LastIndexOf<[0, 0, 0], 2> // -1
2
递归遍历整个数组,使用变量保存下标
点击查看答案
type LastIndexOf<T extends any[], U extends number, M extends number = -1, N extends any[] = []> =
T extends [infer A, ...infer B]
? A extends U
? LastIndexOf<B, U, N['length'], [...N, 0]> : LastIndexOf<B, U, M, [...N, 0]>
: M
2
3
4
5
# Ⓜ️ 实现 Combination
// expected to be `"foo" | "bar" | "baz" | "foo bar" | "foo bar baz" | "foo baz" | "foo baz bar" | "bar foo" | "bar foo baz" | "bar baz" | "bar baz foo" | "baz foo" | "baz foo bar" | "baz bar" | "baz bar foo"`
type Keys = Combination<['foo', 'bar', 'baz']>
2
递归遍历整个数组
点击查看答案
type IsExist<T extends any[], O> =
T extends [infer L, ...infer R]
? L extends O ? true : IsExist<R, O> : false
type Unique<T extends any[], N extends any[] = []> =
T extends [infer L, ...infer R] ?
IsExist<N, L> extends false ? Unique<R, [...N, L]>
: Unique<R, N> : N
2
3
4
5
6
7
8
9
10
11
12
# Ⓜ️ 实现 Filter
type Filtered = FilterOut<[1, 2, null, 3], null> // [1, 2, 3]
点击查看答案
type FilterOut<T extends any[], U> = T extends [infer L, ...infer Rest] ? [L] extends [U] ? [...FilterOut<Rest, U>] : [L, ...FilterOut<Rest, U>] : T
2
3
// todo....
# 🚪 实现 Unique
数组去重
type Res = Unique<[1, 1, 2, 2, 3, 3]>; // expected to be [1, 2, 3]
type Res1 = Unique<[1, 2, 3, 4, 4, 5, 6, 7]>; // expected to be [1, 2, 3, 4, 5, 6, 7]
type Res2 = Unique<[1, "a", 2, "b", 2, "a"]>; // expected to be [1, "a", 2, "b"]
type Res3 = Unique<[string, number, 1, "a", 1, string, 2, "b", 2, number]>; // expected to be [string, number, 1, "a", 2, "b"]
type Res4 = Unique<[unknown, unknown, any, any, never, never]>; // expected to be [unknown, any, never]
2
3
4
5
递归遍历整个数组
点击查看答案
type IsExist<T extends any[], O> =
T extends [infer L, ...infer R]
? L extends O ? true : IsExist<R, O> : false
type Unique<T extends any[], N extends any[] = []> =
T extends [infer L, ...infer R] ?
IsExist<N, L> extends false ? Unique<R, [...N, L]>
: Unique<R, N> : N
2
3
4
5
6
7
8
9
10
11
12
# 🚪 实现 GetMiddleElement
如果是奇数长度,取中间一个数,如果是偶数,取中间两个数。
type simple1 = GetMiddleElement<[1, 2, 3, 4, 5]> // expected to be [3]
type simple2 = GetMiddleElement<[1, 2, 3, 4, 5, 6]> // expected to be [3, 4]
2
递归遍历整个数组,判断长度什么时候可以递归结束;以及使用infer如何提取中间内容
点击查看答案
type GetMiddleElement<T extends any[]> =
T['length'] extends 0 | 1 | 2
? T : T extends [any, ...infer R, any] ? GetMiddleElement<R> :never
2
3
# Ⓜ️ 实现 Appear only once
查找目标数组中只出现一次的元素。例如:输入:[1,2,3,3,4,5,6,6,6],输出:[1,4,5]。
type simple1 = GetMiddleElement<[1, 2, 3, 4, 5]> // expected to be [3]
type simple2 = GetMiddleElement<[1, 2, 3, 4, 5, 6]> // expected to be [3, 4]
2
可以使用一个辅助类判断是否一个元素是否重复,重复不添加,反之添加
点击查看答案
// 辅助类 是否重复
type IsRepeated<T extends any[], U, N extends any[] = []> =
T extends [infer L, ...infer R] ? L extends U
? IsRepeated<R, U, [...N, L]> : IsRepeated<R, U, N> : N['length'] extends 0 | 1 ? false : true
// 结果
type UniqueArray<T extends any[], O extends any[] = T, U extends any[] = []> =
T extends [infer L, ...infer R] ?
IsRepeated<O, L> extends true ? UniqueArray<R, O, U> : UniqueArray<R, O, [...U, L]> : U
2
3
4
5
6
7
8
9
# 🚪 实现数组扁平化
type flatten = Flatten<[1, 2, [3, 4], [[[5]]]]> // [1, 2, 3, 4, 5]
递归
点击查看答案
// 解法一
type Flatten<T extends any[], O extends any[] = []> =
T extends [infer F, ...infer R] ? (
(F extends any[] ? Flatten<[...F, ...R], O> : Flatten<R, [...O, F]>)
) : O
// 解法二 不使用额外参数
type F<T extends any[]> =
T extends [infer L, ...infer R]
? L extends any[] ? [...F<L>, ...F<R>] : [L, ...F<R>] : T
2
3
4
5
6
7
8
9
10
# 🚪 指定深度扁平化
type a = FlattenDepth<[1, 2, [3, 4], [[[5]]]], 2> // [1, 2, 3, 4, [5]]. flattern 2 times
type b = FlattenDepth<[1, 2, [3, 4], [[[5]]]]> // [1, 2, 3, 4, [[5]]]. Depth defaults to be 1
2
参考数组深度扁平化,可以利用数组 `length` 属性,实现指定长度判断
点击查看答案
// 先实现看看 深度递归扁平化
type FlattenDepth<T extends any[], U extends number = 1, D extends any[] = []> =
T extends [infer L, ...infer R]
? (L extends any[] ? [...FlattenDepth<L, U, [...D, 1]>, ...FlattenDepth<R, U, D>] : [L, ...FlattenDepth<R, U, D>]) : T
// 加上长度判断就可以实现指定深度扁平化了
// 答案
type FlattenDepth<T extends any[], U extends number = 1, D extends any[] = []> =
D['length'] extends U
? T : T extends [infer L, ...infer R]
? (L extends any[] ? [...FlattenDepth<L, U, [...D, 1]>, ...FlattenDepth<R, U, D>] : [L, ...FlattenDepth<R, U, D>]) : T
2
3
4
5
6
7
8
9
10
11
12
# Ⓜ️ 实现指定删除数组内容
type Res = Without<[1, 2], 1>; // expected to be [2]
type Res1 = Without<[1, 2, 4, 1, 5], [1, 2]>; // expected to be [4, 5]
type Res2 = Without<[2, 3, 2, 3, 2, 3, 2, 3], [2, 3]>; // expected to be []
2
3
解法有很多,判断内容是否存在与指定内容中,或者使用 联合类型,
点击查看答案
// 解法一 转换成联合类型
type ToUnion<T extends any> = T extends any[] ? T[number] : T
type Without<T extends any[], U extends any | any[]> = T extends [infer L, ...infer R] ?
L extends ToUnion<U> ? Without<R, U> : [L, ...Without<R, U>] : T
// 解法二 判断法
type IsExist<R, T extends any | any[]> =
T extends [infer A, ...infer B] ? R extends A ? true : IsExist<R, B> : R extends T ? true : false
type Without<T extends any[], U, O extends any[] = []> =
T['length'] extends 0 ? O
:
T extends [infer L, ...infer R]
?
IsExist<L, U> extends true ? Without<R, U, O> : Without<R, U, [L, ...O]>
: O
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
拓展
- 尝试使用
Pop
或者shift
方法实现!
# Ⓜ️ 实现给定长度的数组
type result = ConstructTuple<2> // expect to be [unknown, unkonwn]
点击查看答案
type ConstructTuple<T extends number, R extends any[] = []> = R['length'] extends T ? R : ConstructTuple<T, [...R, unknown]>
# 🍅 实现矩阵变换
type Matrix = Transpose <[[1]]>; // expected to be [[1]]
type Matrix1 = Transpose <[[1, 2], [3, 4]]>; // expected to be [[1, 3], [2, 4]]
type Matrix2 = Transpose <[[1, 2, 3], [4, 5, 6]]>; // expected to be [[1, 4], [2, 5], [3, 6]]
2
3
点击查看答案
type ConstructTuple<T extends number, R extends any[] = []> = R['length'] extends T ? R : ConstructTuple<T, [...R, unknown]>
# ReadOnly 系列
# 🚪 实现 Readonly
interface Todo {
title: string
description: string
}
const todo: MyReadonly<Todo> = {
title: "Hey",
description: "foobar"
}
todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property
2
3
4
5
6
7
8
9
10
11
12
点击查看答案
type MyReadonly<T> = {
+readonly [key in keyof T]: T[key]
}
2
3
# Ⓜ️ 实现 DeepReadOnly
type X = {
name: 'a',
age: 12,
b: {
c: {
a: 'role'
}
}
}
type Expected = {
readonly x: {
readonly a: 1
readonly b: 'hi'
}
readonly y: 'hey'
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
提示
我个人感觉这种写法不好理解
- keyof
- in
- extends
- never
点击查看答案
type DeepReadonly<T> = keyof T extends never ? T : { readonly [k in keyof T]: DeepReadonly<T[k]> };
# Ⓜ️ 实现 Mutable
实现一个通用的类型 Mutable<T>
,使类型 T
的全部属性可变(非只读)。
interface Todo {
readonly title: string
readonly description: string
readonly completed: boolean
}
type MutableTodo = Mutable<Todo> // { title: string; description: string; completed: boolean; }
2
3
4
5
6
7
8
**+readonly** 表示只读, **-readonly** 表示可读写
点击查看答案
type Mutable<T extends object = {}> = {
-readonly [key in keyof T]: T[key]
}
2
3
# Ⓜ️ 实现 DeepMutable
实现递归可读写
点击查看答案
type DeepReadonly<T> = keyof T extends never ? T : { -readonly [k in keyof T]: DeepReadonly<T[k]> };
# 字符串
# 🚪 实现字符串首字母大写
type capitalized = Capitalize<'hello world'> // expected to be 'Hello world'
`ts` 中字符串转换成大写 `Uppercase`;小写是 `Lowercase`
点击查看答案
type Capitalize<T extends string> = T extends `${infer F}${infer L}` ? `${Uppercase<F>}${L}` : T
2
拓展
- 实现 全部大写
- 实现 全部小写
# 🚪 实现字符串替换
type replaced = Replace<'types are fun!', 'fun', 'awesome'> // expected to be 'types are awesome!'
使用infer提取,需要考虑空字符串情况
点击查看答案
type Replace<S extends string, F extends string, T extends string> = S extends '' ? S : (
S extends `${infer L}${F}${infer R}`? `${L}${T}${R}`: S
)
2
3
拓展
实现全局替换
type replaced = ReplaceAll<'fun types fun are fun!', 'fun', 'awesome'> // expected to be 'awesome types awesome are awesome!'
根据上面使用递归就可以了
点击查看答案
type ReplaceAll<S extends string, F extends string, T extends string> = S extends '' ? S : (
S extends `${infer L}${F}${infer R}` ? ReplaceAll<`${L}${T}${R}`, F, T> : S
)
2
3
# 🚪 实现获取字符串长度
字符串是是无法直接获取长度,转换成数组,通过递归遍历然后获取
点击查看答案
type StrLen<T extends string, Arr extends string[] = []> = T extends `${infer F}${infer R}` ? StrLen<R, [...Arr, F]> : A['length']
// eg type B = StrLen<"javascript"> // 10
# 🚪 实现字符串转换联合类型
实现一个接收string,number或bigInt类型参数的Absolute类型,返回一个正数字符串
type Test = '123';
type Result = StringToUnion<Test>; // expected to be "1" | "2" | "3"
2
用infer递归处理
点击查看答案
type StringToUnion<T extends string> = T extends `${infer F}${infer R}` ? F | StringToUnion<R> : never
# 🚪 实现是否是给定字符串开头
实现StartsWith<T, U>,接收两个string类型参数,然后判断T是否以U开头,根据结果返回true或false
type a = StartsWith<'abc', 'ac'> // expected to be false
type b = StartsWith<'abc', 'ab'> // expected to be true
type c = StartsWith<'abc', 'abcd'> // expected to be false
2
3
点击查看答案
type StartsWith<T extends string, U extends string> = T extends `${U}${infer R}` ? true : false
2
拓展 判断是否是指定字符串结尾
type End<T extends string, U extends string> = T extends `${infer R}${U}` ? true : false
2
:::
# 🚪 实现去掉字符串空格
type trimed = TrimLeft<' Hello World '> // expected to be 'Hello World
type trimed1 = TrimRright<' Hello World '> // expected to be 'Hello World
type trimed2 = Trim<' Hello World '> // expected to be 'Hello World
2
3
把字符串当数组理解,数组是如何提取,字符串就如何提取
递归
infer
查看答案
// 一次性解决吧
type S = ' '
type TrimLeft<T extends string> = T extends `${S}${infer R}` ? TrimLeft<R>:T
type TrimRright<T extends string> = T extends `${infer R}${S}` ? TrimRright<R>:T
type Trim<T extends string> = T extends `${S}${infer R}${S}` ? Trim<R>:T
// 看了答案s = ' '不准确,使用 S = ' ' | '\n' | '\t'
2
3
4
5
6
# 🚪 实现删除符合要求的字符串
type Butterfly = DropChar<' b u t t e r f l y ! ', ' '> // 'butterfly!'
递归!!!注意保存每次删除之后的值
点击查看答案
type DropChar<T extends string, S extends string = ''> = T extends `${infer L}${S}${infer R}`
? DropChar<`${L}${R}`, S> : T
2
# Ⓜ️ 实现驼峰命名转换短横线命名
type FooBarBaz = KebabCase<"FooBarBaz">;
const foobarbaz: FooBarBaz = "foo-bar-baz";
type DoNothing = KebabCase<"do-nothing">;
const doNothing: DoNothing = "do-nothing";
type D = Uncapitalize<"FooBarBaz">
2
3
4
5
6
用infer递归处理
点击查看答案
// 第一步写出基本形式
type KebabCase<S extends string> = S extends `${infer S1}${infer S2}` ? `${Uncapitalize<S1>}${Uncapitalize<S2>}`:S
// 判断 S2 开头是否大写,如果是大写,转换成小写,同时在前面添加短横线,
// 递归 infer 提取的 S2
type KebabCase<S extends string> = S extends `${infer S1}${infer S2}` ? (
S2 extends `${Uncapitalize<S2>}`? `${Uncapitalize<S1>}${KebabCase<S2>}`: `${Uncapitalize<S1>}-${KebabCase<S2>}`
):S
2
3
4
5
6
7
8
# Ⓜ️ 现判断一个字符串中字符是否重复
type S1 = CheckRepeatedChars<'abc'> // false
type S2 = CheckRepeatedChars<'aba'> // true
2
可以参考数组判断重复,借助辅助函数判断是否重复
点击查看答案
// 辅助函数 判断指定字符串是否重复
type IsRepeated<T extends string, U extends string, M extends any[] = []> =
T extends `${infer L}${infer R}` ?
U extends L ? IsRepeated<R, U, [...M, L]> : IsRepeated<R, U, M>
: M['length'] extends 1 ? false : true
// 结果
type CheckRepeatedChars<T extends string, O extends string = T> =
T extends `${infer L}${infer R}`
? IsRepeated<O, L> extends true ? true : CheckRepeatedChars<R, O> : false
2
3
4
5
6
7
8
9
10
11
# Ⓜ️ 实现 单词首字母大写
实现CapitalWords
,它将字符串中每个单词的第一个字母转换为大写字母,其余部分保持原样。
type H1 = CapitalizeWords<"A"> // => 'A'
type H2 = CapitalizeWords<"AB">// => 'Ab'
type H3 = CapitalizeWords<"ABC"> // => 'Abc'
type H4 = CapitalizeWords<" ABC"> // => " Abc"
type H5 = CapitalizeWords<" ABC "> // => ' Abc '
type H6 = CapitalizeWords<" ABC aDmin"> // => ' Abc Admin '
type H7 = CapitalizeWords<"Hello world hElLO WORlD"> // => Hello World Hello World
2
3
4
5
6
7
分析
- 本题解法同capitalizewords (opens new window)
- 使用虚拟头节点,将 Head 指向上一次遍历的字符串
点击查看答案
type Line = "_" | "-" | "__"
type CamelCase<T extends string, Head extends string = "", W extends string = "",> = T extends `${infer L}${infer R}`
? L extends Line ? CamelCase<R, L, W> : Head extends Line ? CamelCase<R, L, `${W}${Uppercase<L>}`> : CamelCase<R, L, `${W}${Lowercase<L>}`> : W extends `${infer L}${infer R} ` ? `${Lowercase<L>}${R} ` : W
2
3
4
5
# 🍅 实现 CamelCase
type camelCase1 = CamelCase<'hello_world_with_types'> // expected to be 'helloWorldWithTypes'
type camelCase2 = CamelCase<'HELLO_WORLD_WITH_TYPES'> // expected to be same as previous one
2
分析
隐含条件,不只是将第一个单词大写,还必须将后面相连的字母小写!!!
本题难度在于如果只有一个字符串情况下第一个首字母大写,如
A
= >A
,a
=A
,AB
= >Ab
,ab
=>Ab
,aB
=>Ab
如果直接判断话容易错误,该题有点像链表遍历,因此采用虚拟头节点,不管有没有内容,头节点默认为 ""[空字符串]
点击查看答案
// 空字符串情况
type Space = "" | " " | "\n" | "\t"
// Head 为虚拟头节点,默认为 ""
// 不仅要判断头节点为空!还要判断下一个节点是否为空
type CapitalizeWords<S extends string, Head extends string = "", W extends string = ""> =
S extends `${infer L}${infer R}` ? Head extends Space ? L extends Space ?
CapitalizeWords<Lowercase<R>, L, `${W}`> :
CapitalizeWords<Lowercase<R>, L, `${W}${Uppercase<L>}`> : CapitalizeWords<Lowercase<R>, L, `${W}${L}`> : W
2
3
4
5
6
7
8
9
10
# 🍅 实现 LengthOfString
这个问题不同于上面 实现获取字符串长度
type T0 = LengthOfString<"foo"> // 3
问题不难!但它们都受到TypeScript的递归限制的限制,这是这个问题的主要约束
# 函数系列
# 🚪 实现获取函数返回类型
不使用 ReturnType 实现 TypeScript 的 ReturnType<T>
泛型。
example:
const fn = (v: boolean) => {
if (v)
return 1
else
return 2
}
type a = MyReturnType<typeof fn> // 应推导出 "1 | 2"
2
3
4
5
6
7
8
提示
ts中函数表示方式,可以使用关键词infer
提取函数返回类型
点击查看答案
type MyReturnType<T> = T extends (...args: any) => infer R ? R : never
# Ⓜ️ 实现函数参数反转
本题可以参考数组反转
type Flipped = FlipArguments<(arg0: string, arg1: number, arg2: boolean) => void>
// (arg0: boolean, arg1: number, arg2: string) => void
2
点击查看
// 提供一个反转数组类型
type Reverse<T extends any[]> = T extends [...infer F, infer R] ? [R, ...Reverse<F>] : T
// 参数反转
type FlipArguments<T extends (...args: any[]) => any> =
T extends (...args: infer Args) => infer R ?
(...args: Reverse<Args>) => R : never
2
3
4
5
6
7
# 🍅 实现参数追加
type Fn = (a: number, b: string) => number
type Result = AppendArgument<Fn, boolean>
// expected be (a: number, b: string, x: boolean) => number
2
3
4
函数表现形式以及参数和返回类型提取
# 🍅 实现柯里化
const add = (a: number, b: number) => a + b
const three = add(1, 2)
const curriedAdd = Currying(add)
const five = curriedAdd(2)(3)
2
3
4
5
柯理华本质是看函数有几个参数,多少个参数返回多少个函数,使用递归
点击查看答案
declare function Currying<F>(fn: F): Curried<F>
// 原函数返回
type Curried<F> = F extends (...args: infer Args) => infer R ?
(...arg: Args) => R
: never
// 判断函数参数个数,使用递归 【答案】
type Curried<F> = F extends (...args: infer Args) => infer R ?
Args extends [infer First, ...infer Other] ? (arg: First) => Curried<(...args: Other) => R> : R
: never
2
3
4
5
6
7
8
9
10
11
12
13
14
# Object 系列
# 🚪 实现为一个接口添加新类型
实现一个为接口添加一个新字段的类型。该类型接收三个参数,返回带有新字段的接口类型。
type Test = { id: '1' }
type Result = AppendToObject<Test, 'value', 4> // expected to be { id: '1', value: 4 }
2
构造一个K,V给对象,参考Record实现
点击查看答案
type AppendToObject<T, U extends keyof any, V> = {
[K in keyof T | U]: K extends keyof T ? T[K] : V
}
2
3
# Ⓜ️ 实现连两个对象合并
type foo = {
name: string;
age: string;
}
type coo = {
age: number;
sex: string
}
type Result = Merge<foo,coo>; // expected to be {name: string, age: number, sex: string}
2
3
4
5
6
7
8
9
10
参考上面给对象添加新属性案例,注意后面对象同名属性必须要覆盖前面对象这个要求
点击查看答案
type Merge<T, U, K extends keyof T = keyof T, J extends keyof U = keyof U> = {
[key in J | K]: key extends J ? U[key] : (key extends K ? T[key] : never)
}
2
3
# Ⓜ️ 实现可配置可选链
declare const config: Chainable
const result = config
.option('foo', 123)
.option('name', 'type-challenges')
.option('bar', { value: 'Hello World' })
.get()
// expect the type of result to be:
type TR = typeof result;
interface Result {
foo: number
name: string
bar: {
value: string
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
提示
- 考虑属性是否存在类型中
- 建造者模式(option 方法类似于建造者模式)
- 方法表达式
点击查看答案
// 第一步写出最简单形式
type Chainable<T = {}> = {
option: <K extends string, V>(k: K, v: V) => T,
get: () => T
};
// 第二部考虑 k 是否存在与 T 中
type Chainable<T = {}> = {
option: <K extends string, V>(k: K extends keyof T ? never : K, v: V) => T,
get: () => T
};
// 第三步 如果将 K V 对应属性添加到 T 中 这里使用 Omit<T,K>
type Chainable<T = {}> = {
option: <K extends string, V>(k: K extends keyof T? never : K, v: V) => Chainable<Omit<T,K>>,
get: () => T
};
// 第四步,使用 Record 合并属性
type Chainable<T = {}> = {
option: <K extends string, V>(k: K extends keyof T? never : K, v: V) => Chainable<Omit<T,K>&Record<K,V>>,
get: () => T
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
点击查看答案
// 第一步写出形式
type AppendArgument<Fn, A> = Fn extends (...args: any[]) => void?(...args: any[]) => void:never
// 第二步使用infer提取参数和返回类型
type AppendArgument<Fn, A> = Fn extends (...args: infer P) => infer T ? (...args: [...P, A]) => T : never
2
3
4
5
# Ⓜ️ 实现 Diff
获取两个接口类型中的差值属性。
type Foo = {
a: string;
b: number;
}
type Bar = {
a: string;
c: boolean
}
type Result1 = Diff<Foo,Bar> // { b: number, c: boolean }
type Result2 = Diff<Bar,Foo> // { b: number, c: boolean }
2
3
4
5
6
7
8
9
10
11
12
13
点击查看答案
//
// 解法一、我自己解法 太复杂了😴
// [key in K | J as key extends K & J ? never : key]
// key 属于并集 单不属于 交集
type Diff<T, U, K extends keyof T = keyof T, J extends keyof U = keyof U> = {
[key in K | J as key extends K & J ? never : key]:
key extends K ? key extends J ? never : T[key]
: (key extends J ? (key extends K ? never : U[key]) : never)
}
// 解法二
// [key in keyof (U & T) as key extends keyof (T | U) ? never : key]
// key 属于 T U的并集 单不属于交集
type Diff<T, U> = {
[key in keyof (U & T) as key extends keyof (T | U) ? never : key]: (U & T)[key]
}
// 解法三,使用 Omit
// 注意了解 Omit 的用法,
type Diff<T, U> = Omit<T & U, keyof (U | T)>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
拓展
// 如果是对象类型,
// | 表示 交集,& 表示并集
// 交集
type A = keyof (Foo | Bar) // type A = "a"
// 并集
type B = keyof (Foo & Bar) // type B = "a" | "b" | "c"
// 对于联合类型
// | 表示 并集, & 表示 交集
// 并集
type C = (keyof Foo) | (keyof Bar) // type C = "a" | "b" | "c"
// 交集
type D = (keyof Foo) & (keyof Bar) // type D = "a"
2
3
4
5
6
7
8
9
10
11
12
13
14
# Ⓜ️ 实现属性替换
type NodeA = {
type: 'A'
name: string
flag: number
}
type NodeB = {
type: 'B'
id: number
flag: number
}
type NodeC = {
type: 'C'
name: string
flag: number
}
type Nodes = NodeA | NodeB | NodeC
// {type: 'A', name: number, flag: string}
// |{type: 'B', id: number, flag: string}
// | {type: 'C', name: number, flag: string}
type ReplacedNodes = ReplaceKeys<Nodes, 'name' | 'flag', { name: number, flag: string }>
//
// {type: 'A', name: never, flag: number}
// | NodeB
// | {type: 'C', name: never, flag: number} // would replace name to never
type ReplacedNotExistKeys = ReplaceKeys<Nodes, 'name', { aa: number }>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
点击查看答案
// 我的解法
type ReplaceKeys<T, U, S> = T extends T ?
{
[key in keyof T]: key extends U ? (
key extends keyof S ? S[key] : never
) : key extends keyof S ? S[key] : T[key]
}
: never
// 参考答案
type ReplaceKeys<U, T, Y> = U extends U
? {
[I in keyof U]: I extends T ? (I extends keyof Y ? Y[I] : never) : U[I];
}
: never;
// 参考答案
type ReplaceKeys<U, T, Y> = {
[K in keyof U]: K extends T ? (K extends keyof Y ? Y[K] : never) : U[K]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 🤔 实现移除索引签名
Only some types are allowed for index signature properties:
string
,number
,symbol
, template string patterns, and union types consisting only of these.
索引签名属性只允许使用某些类型:字符串、数字、符号、模板字符串模式以及仅由这些类型组成的并集类型。
type Foo = {
[key: string]: any;
foo(): void;
}
type A = RemoveIndexSignature<Foo> // expected { foo(): void }
2
3
4
5
6
7
**ts**自带索引签名类型的联合类型**PropertyKey**
点击查看答案
type S = string | symbol | number
type RemoveIndexSignature<T, P = S> = {
[key in keyof T as P extends key ? never : key extends P? key : never]: T[key]
}
2
3
4
5
# 🤔 实现 Entries
从T中,选择一组类型不可分配给U的属性。
interface Model {
name: string;
age: number;
locations: string[] | null;
}
type modelEntries = ObjectEntries<Model> // ['name', string] | ['age', number] | ['locations', string[] | null];
2
3
4
5
6
点击查看答案
type ObjectEntries<T, U extends keyof T = keyof T> = U extends unknown
? [U, T[U] extends (infer F | undefined)
? F
: T[U]
]
: never
2
3
4
5
6
# 🤔 实现 元组递归转换成对象
从T中,选择一组类型不可分配给U的属性。
// 元组转换成对象
type a = TupleToNestedObject<['a'], string> // {a: string}
type b = TupleToNestedObject<['a', 'b'], number> // {a: {b: number}}
type c = TupleToNestedObject<[], boolean> // boolean. if the tuple is empty, just return the U type
type d = TupleToNestedObject<['a', 'b', 'c'], number> // {a: {b: {c:number}}}
2
3
4
5
点击查看答案
type TupleToNestedObject<T extends any[], U> =
T extends [infer L, ...infer R] ? {
[key in L & string]: TupleToNestedObject<R, U>
} : U
2
3
4
5
# 🤔 实现 将元组转换成枚举
type Arr = ["macOS", "Windows", "Linux"]
type A1 = Enum<Arr, false>
// expect
// type A1 = {
// readonly macOS: "macOS";
// readonly Windows: "Windows";
// readonly Linux: "Linux";
// }
type A2 = Enum<Arr, true>
// expect
// type A2 = {
// readonly macOS: 0;
// readonly Windows: 1;
// readonly Linux: 2;
// }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
数组转换成对象 遍历方式
点击查看答案
type UseIndex<T extends readonly unknown[], k extends string, U extends unknown[] = []> = T extends [infer L, ...infer R] ? [L] extends [k] ? U['length']
: UseIndex<R, k, [...U, unknown]> : -1
type Enum<T extends readonly any[], B extends boolean = false> = {
+readonly [K in T[number]]: B extends true ? UseIndex<T, K> : K
}
2
3
4
5
6
7
# Is 系列
# 🍅 实现 IsNever
type A = IsNever<never> // expected to be true
type B = IsNever<undefined> // expected to be false
type C = IsNever<null> // expected to be false
type D = IsNever<[]> // expected to be false
type E = IsNever<number> // expected to be false
2
3
4
5
点击查看答案
type IsNever<T> = [T] extends [never] ? true : false
// 为什么需要括号? 直接 T extends never ? 不行吗
// 测试
type Is_Never<T> = T extends never ? true : false
type A = Is_Never<never>; // => 推算结果为 never 而不是 true 显然不符合题意
// 具体原因请查看下面链接
2
3
4
5
6
7
知识点
想要消除分发特性,用 []
包裹下就行。 TS分发特性 (opens new window)
# 🤔 实现 isUnion
type case1 = IsUnion<string> // false
type case2 = IsUnion<string|number> // true
type case3 = IsUnion<[string|number]> // false
2
3
重点!
A extends A 导致A被分发,所以在[B] extends [A] 这里,B 是联合类型,而A 是分发类型,二者如果不等,那么表示A就是联合类型,具体看
点击查看答案
type IsUnion<A, B = A> = A extends A ? ([B] extends [A] ? false : true) : false;
# ❓ 实现 IsAny
题目描述
Implement a generic IsRequiredKey<T, K>
that return whether K
are required keys of T
.
type A = IsRequiredKey<{ a: number, b?: string },'a'> // true
type B = IsRequiredKey<{ a: number, b?: string },'b'> // false
type C = IsRequiredKey<{ a: number, b?: string },'b' | 'a'> // false
2
3
点击查看答案
// 方案一
type IsAny<T> = [{}, T] extends [T, null] ? true : false;
// https://github.com/type-challenges/type-challenges/issues/5794
// 方案
type IsAny<T> = 0 extends (1 & T) ? true : false;
// 本题答案是查看别人的
// https://github.com/type-challenges/type-challenges/issues/232
2
3
4
5
6
7
8
技巧
any & 任意类型 = any
# 🍅 实现 IsAnyof
在类型系统中实现类似于 Python 中 any 函数。类型接收一个数组,如果数组中任一个元素为真,则返回 true,否则返回 false。如果数组为空,返回 false。
type Sample1 = IsAnyof<[1, '', false, [], {}]> // expected to be true.
type Sample2 = IsAnyof<[0, '', false, [], {}]> // expected to be false.
2
3
对象类型 使用 `keyof` 遍历 属性,数组使用 `[number]` 遍历属性
点击查看答案
type FALSE = 0 | '' | false | [] | { [key: string]: never }
type IsAnyof<T extends any[]> = T[number] extends FALSE
? false : true;
2
3
4
5
拓展
type a1 = {} extends { name: 'a' } ? true : false // false
type b1 = { name: 'a' } extends {} ? true : false // true
type c1 = { [key: string]: never } extends {} ? true : false // true
type d1 = {} extends { [key: string]: never } ? true : false // true
2
3
4
# 🍅 实现 IsRequiredKey
题目描述
Implement a generic IsRequiredKey<T, K>
that return whether K
are required keys of T
.
type A = IsRequiredKey<{ a: number, b?: string },'a'> // true
type B = IsRequiredKey<{ a: number, b?: string },'b'> // false
type C = IsRequiredKey<{ a: number, b?: string },'b' | 'a'> // false
2
3
可以考虑 `Pick` `Required`
点击查看答案
// 方案一
type IsRequiredKey<T, U extends keyof T = keyof T> = Pick<T, U> extends Pick<Required<T>, U> ? true : false
// 方案二
type IsRequiredKey<T, K extends keyof T> = T[K] extends NonNullable<T[K]> ? true : false
// type NonNullable<T> = T & {};
2
3
4
5
6
# 🍅 实现 IsPalindrome
描述
Implement type IsPalindrome<T>
to check whether a string or number is palindrome.
For example:
IsPalindrome<'abc'> // false
IsPalindrome<121> // true
2
递归
点击查看答案
type IsPalindrome<T extends string | number, K = `${T}`> =
K extends `${infer L}${infer R}` ?
R extends '' ? true :
K extends `${L}${infer S}${L}` ? IsPalindrome<S> : false
: true
2
3
4
5
# 中等系列
# Ⓜ️ 实现 Omit
不使用 Omit 实现 TypeScript 的 Omit<T, K> 泛型。
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyOmit<Todo, 'description' | 'title'>
const todo: TodoPreview = {
completed: false,
}
2
3
4
5
6
7
8
9
10
11
提示
- keyof
- in
- extends
- never
点击查看答案
type MyOmit<T, K extends keyof T> = {
[P in keyof T as P extends K ? never : P]: T[P]
}
2
3
# Ⓜ️ 实现元组转换成联合类型
type Arr = ['java', 'javascript', 'typescript', "node", "springboot"]
type Test = TupleToUnion<Arr> // expected to be '1' | '2' | '3'
2
3
4
5
提示
- 递归
- infer
点击查看答案
// 解法一,自己做的
type TupleToUnion<T extends any[]> = T extends [infer F, ...infer L] ? F | TupleToUnion<L> : never
// 解法二 ,说实话,根本看不懂!
// 原答案链接 https://github.com/type-challenges/type-challenges/issues/284
type TupleToUnion<T extends any[]> = T[number]
2
3
4
5
6
7
# Ⓜ️ 实现 Absolute
实现一个接收string,number或bigInt类型参数的Absolute类型,返回一个正数字符串
type Test = -100;
type Result = Absolute<Test>; // expected to be "100"
2
用字符串和infer处理
点击查看答案
type Absolute<T extends string | number | bigint> = `${T}` extends `-${infer R}` ? R : `${T}`
拓展:如何获取绝对值呢?
# 🤔 实现 MinusOne
给定一个正整数作为类型的参数,要求返回的类型是该数字减 1。
type Zero = MinusOne<1> // 0
type FiftyFour = MinusOne<55> // 54
2
ts中是无法直接减一的,但是根据题目要求,可以联想数组,数组的长度和最大下标就是差一!
点击查看答案
type MinusOne<T, P extends any[] = []>
= P['length'] extends T ? P extends [...infer F, any] ? F['length'] : []['length'] : MinusOne<T, [...P, 0]>
2
3
# Ⓜ️ 实现 PickByType
挑选出类型
type OnlyBoolean = PickByType<{
name: string
count: number
isReadonly: boolean
isEnable: boolean
}, boolean> // { isReadonly: boolean; isEnable: boolean; }
2
3
4
5
6
点击查看答案
// 解法一
type PickByType<T extends object = {}, U = any> = {
[K in keyof T as T extends K ? never : T[K] extends U ? K : never]: T[K]
}
// 解法二 是要 pick 注意 {}[keyof T] 的含义
type PickByType<T, U> = { [K in keyof T]: T[K] extends U ? K : never }[keyof T]
// 解法三 和 解法一类似
type PickByType<T extends Object, U> = {
[Key in keyof T as T[Key] extends U ? Key : never]: T[Key]
}
2
3
4
5
6
7
8
9
10
11
12
13
# 🍅 实现 PartialByKeys
实现一个通用的PartialByKeys<T, K>
,它接收两个类型参数T
和K
。
K
指定应设置为可选的T
的属性集。当没有提供K
时,它就和普通的Partial<T>
一样使所有属性都是可选的。
interface User {
name: string
age: number
address: string
}
type UserPartialName = PartialByKeys<User, 'name'> // { name?:string; age:number; address:string }
2
3
4
5
6
7
点击查看答案
type IO<T extends object = {}> = {
[k in keyof T]: T[k]
}
type PartialByKeys<T extends object = {}, K = any> =
IO<
{
[P in keyof T as P extends K ? P : never]?: T[P]
}
&
{
[P in keyof T as P extends K ? never : P]: T[P]
}
>
// 解法二
type PartialByKeys<T extends object = {}, K = any> =
IO<
{
[P in Extract<keyof T, K>]?: T[P]
}
&
{
[P in Exclude<keyof T, K>]: T[P]
}
>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
提示
- Extract 期望类型
- Exclude 排除类型
- 联合类型中
&
表示交集,|
表示并集;对象类型中&
表示并集,|
表示交集
# Ⓜ️ 实现 OmitByType
从T中,选择一组类型不可分配给U的属性。
type OmitBoolean = OmitByType<{
name: string
count: number
isReadonly: boolean
isEnable: boolean
}, boolean> // { name: string; count: number }
2
3
4
5
6
点击查看答案
type OmitByType<T, U> = {
[P in keyof T as T[P] extends U ? never : P]: T[P]
}
2
3
# Ⓜ️ Bem 架构
块、元素、修饰符方法论(BEM)是CSS中常用的类命名约定。例如,块组件将表示为btn,依赖于块的元素将表示为btn_price,改变块样式的修饰符将表示为btn-big或btn_price-warning。实现BEM<B,E,M>,根据这三个参数生成字符串并集。其中B是字符串文字,E和M是字符串数组(可以为空)。
type A = BEM<'el', ['button', 'button'], ['primary', 'success']>
// "el__button--primary" | "el__button--success"
2
点击查看答案
type IsNever<T> = [T] extends [never] ? true : false
type IsUnion<T> = IsNever<T> extends true ? "" : T
type BEM<B extends string, E extends string[], M extends string[]> = `${B}${IsUnion<`__${E[number]}`>}${IsUnion<`--${M[number]}`>}`
2
3
# 🍅 指定返回的联合类型
type result = NumberRange<2, 9> // 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
点击查看答案
// 根据数字生成数组
type AppendArray<R extends number, T extends any[] = []> = T['length'] extends R ? T : AppendArray<R, [...T, T['length']]>
// 数值加一
type AddLength<T extends any[]> = [...T, T['length']]
// 去掉指定前面个数
type Shfit<T extends any[], N extends number, U extends any[] = []> =
U['length'] extends N ? T : T extends [any, ...infer R] ? Shfit<R, N, [...U, 0]> : T
// 数组转换联合类型
type ArrayToUnion<T extends any[]> = T[number]
// result
type NumberRange<T extends number, R extends number> = ArrayToUnion<Shfit<AddLength<AppendArray<R>>, T>>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 未完待续……
# 困难系列
# 🍅 实现 GetRequired
实现高级util类型GetRequired<T>
,该类型保留所有必填字段
type I = GetRequired<{ foo: number, bar?: string }> // expected to be { foo: number }
点击查看答案
// 解法一
// 使得所有key变成必须选类型
type MyRequried<T> = {
[k in keyof T]-?: T[k]
}
type GetRequired<T> = {
[key in keyof T as T[key] extends MyRequried<T>[key] ? key : never]: T[key]
}
// 解法二
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 🍅 实现 GetRequiredkeys
type Result = RequiredKeys<{ foo: number; bar?: string }>;
// expected to be “foo”
2
和上面解法类似,对象类型转换成联合类型
点击查看答案
type MyRequried<T> = {
[key in keyof T]-?: T[key]
}
type GetRequired<T> = {
[key in keyof T as T[key] extends MyRequried<T>[key] ? key : never]: key
}
type RequiredKeys<T> = GetRequired<T>[keyof GetRequired<T>]
2
3
4
5
6
7
8
9
拓展
- Array类型转换成联合类型
# 🍅 实现 GetOptional
保留可选类型的key,转换成联合类型
type I = GetOptional<{ foo: number, bar?: string }> // expected to be { bar?: string }
和上面解法类似,只不过改变了一下判断依据
点击查看答案
type MyRequired<T> = {
[key in keyof T]-?: T[key]
}
type GetOptional<T> = {
[key in keyof T as T[key] extends MyRequired<T>[key] ? never : key]: T[key]
}
2
3
4
5
6
7
8
9
10
# 🍅 实现 GetOptionalKeys
保留可选类型的key,转换成联合类型
type I = GetOptional<{ foo: number, bar?: string }> // expected to be "bar"
点击查看答案
type MyRequired<T> = {
[key in keyof T]-?: T[key]
}
type GetOptional<T> = {
[key in keyof T as T[key] extends MyRequired<T>[key] ? never : key]: key
}
// 将得到的可选类型转换成必须类型,去除 undefined
type OptionalKeys<T> = MyRequired<GetOptional<T>>[keyof MyRequired<GetOptional<T>>]
2
3
4
5
6
7
8
9
10
11
12
13
# 🍅 实现 SimpleVue
实现类似Vue的类型支持的简化版本。
通过提供一个函数SimpleVue
(类似于Vue.extend
或defineComponent
),它应该正确地推断出 computed 和 methods 内部的this
类型。
在此挑战中,我们假设SimpleVue
接受只带有data
,computed
和methods
字段的Object作为其唯一的参数,
data
是一个简单的函数,它返回一个提供上下文this
的对象,但是你无法在data
中获取其他的计算属性或方法。computed
是将this
作为上下文的函数的对象,进行一些计算并返回结果。在上下文中应暴露计算出的值而不是函数。methods
是函数的对象,其上下文也为this
。函数中可以访问data
,computed
以及其他methods
中的暴露的字段。computed
与methods
的不同之处在于methods
在上下文中按原样暴露为函数。
SimpleVue
的返回值类型可以是任意的。
const instance = SimpleVue({
data() {
return {
firstname: 'Type',
lastname: 'Challenges',
amount: 10,
}
},
computed: {
fullname() {
return this.firstname + ' ' + this.lastname
}
},
methods: {
hi() {
alert(this.fullname.toLowerCase())
}
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
点击查看答案
type GetComputed<C> = C extends Record<string, (...args: any[]) => any>
? { [S in keyof C]: ReturnType<C[S]> }
: never
declare function SimpleVue<D, C, M>(
options: {
data: () => D,
computed: C,
methods: M,
} & ThisType<D & M & GetComputed<C>>
): any
2
3
4
5
6
7
8
9
10
11
# 🍵 实现将字符串转换成数字
点击查看
type ToNumber<S extends string> = S extends `${infer N extends number}` ? N : never;
这也是难题?
# 🤔 实现两数之和
给定一个整数数组 nums 和一个目标整数 target, 如果 nums 数组中存在两个元素的和等于 target 返回 true, 否则返回 false
理清思路,排序,左右指针
点击查看
// 方法一使用函数实现
function TowSum(nums: number[], target: number): boolean {
if (nums['length'] < 2) {
return false
}
nums.sort((a, b) => a - b > 0 ? 1 : -1)
let left = 0, right = nums['length'] - 1
let sum: number
while (left < right) {
sum = nums[left] + nums[right]
if (sum === target) {
return true
} else if (sum > target) {
right--;
} else if (sum < target) {
left++;
}
}
return false
}
// 方法二使用类型实现
// 暂时搁置...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 📅 实现日期验证
要求实现一个类型验证月份和日期
ValidDate<'0102'> // true
ValidDate<'0131'> // true
ValidDate<'1231'> // true
ValidDate<'0229'> // false
ValidDate<'0100'> // false
ValidDate<'0132'> // false
ValidDate<'1301'> // false
2
3
4
5
6
7
本题可以采用枚举实现,知道哪些月份是月大,哪些是月小,以及二月份是一个特殊月份!
点击查看答案
// 数字的范围
type Num = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
type D = 0 | 1 | 2
// 月小
type MinMM = `0${4 | 6 | 7}` | `11`
// 月大
type MaxMM = Exclude<`0${Num}` | `1${D}`, MinMM | `02`>
type Day = `${D}${Num}` | `3${Exclude<D, 2>}`
type ValidDate<T extends string> = T extends `${MaxMM}${Day}` | `${MinMM}${Exclude<Day, `31`>}` | `02${Exclude<Day, `29` | `30` | `31`>}` ? true : false
2
3
4
5
6
7
8
9
10
11
12
13
14
# 🍰 实现 Get
实现一个Get类型,获取对象属性
type Data = {
foo: {
bar: {
value: 'foobar',
count: 6,
},
included: true,
},
hello: 'world'
}
type A = Get<Data, 'hello'> // 'world'
type B = Get<Data, 'foo.bar.count'> // 6
type C = Get<Data, 'foo.bar'> // { value: 'foobar', count: 6 }
2
3
4
5
6
7
8
9
10
11
12
13
14
判断 属性中是否含有`.`是切入点
点击查看答案
type Get<T, K extends string> = K extends `${infer L}.${infer R}` ? L extends keyof T ? Get<T[L], R> : never : K extends keyof T ? T[K] : never
# 🍅 实现 Maximum
题目描述
Implement the type Maximum
, which takes an input type T
, and returns the maximum value in T
.
If T
is an empty array, it returns never
. Negative numbers are not considered.
Maximum<[]> // never
Maximum<[0, 2, 1]> // 2
Maximum<[1, 20, 200, 150]> // 200
2
3
难题单元化,多使用辅助类型
点击查看答案
// 设置一个默认值为0
// 当 C['length'] === A | B 时退出递归
// 判断 C['length'] === A or C['length'] === B 先 === 的为较小值
// 不过这种方式当 A B 值为 负数时 该方案错误!
// 因此分为四种情况
// 1 A>= 0 B>=0 ===========> 比较 length ,谁的 length 大 返回谁
// 2 A>0 B<0 ===========> 直接返回 A
// 3 A<0 B>0 ===========> 直接返回 B
// 4 A<0 B<0 ========== > 谁的length小,值越大
// isAllNegative 表示两个数是否为负数
// A B 表示绝对值
// A1 表示 A 对应 如果A 是正数,L 和也是正数 如果 A = 1 ,L = 1 =>1; A = 1,A1 = -1 => -1
// B1 同 A1与 A 对应关系
type Compare<A extends number, B extends number, isAllNegative extends boolean = false, A1 extends number = A, B1 extends number = B, C extends any[] = []> =
C['length'] extends A | B ?
C['length'] extends A ? isAllNegative extends true ? A1 : B1 : isAllNegative extends true ? B1 : A1
: Compare<A, B, isAllNegative, A1, B1, [...C, unknown]>
// 辅助类型
// 判断是否是负数
type IsNegative<T extends number> = `${T}` extends `-${infer A extends number}` ? true : false
// 获取绝对值
type GetNegative<T extends number> = `${T}` extends `-${infer A extends number}` ? A : T
// 两个负数数字越小值越大!
// 如果是一个正数和一个负数 就没必要对比了直接返回正数
type Max<A extends number, B extends number> =
IsNegative<A> extends true
? IsNegative<B> extends true
// A < 0 , B < 0
? Compare<GetNegative<A>, GetNegative<B>, true, A, B> : B
: IsNegative<B> extends true ? A : Compare<A, B>
type Maximum<T extends number[], M extends number = T[0], First extends boolean = true> =
T['length'] extends 0 ? (
First extends true ? never : M
) :
T extends [infer A extends number, ...infer B extends number[]] ?
Maximum<B, Max<A, M>, false> : M
// test
type A1 = Max<10, 2> // 10
type A2 = Max<2, 10> // 10
type B1 = Max<0, 1> // 1
type B2 = Max<0, -1> // 0
type C1 = Max<-1, 1> // 1
type C2 = Max<1, -1> // 1
type D1 = Max<-2, -1> // -1
type D2 = Max<-1, -2> // -1
type E1 = Max<-100, -200> // -100
type E2 = Max<-200, -100> // -100
// test
type arr0 = Maximum<[]> // never
type arr1 = Maximum<[-1, 20, -200, -150]> // 20
type arr2 = Maximum<[-1, -20, -200, -150]> // -1
type arr3 = Maximum<[1, 20, 200, -150]> // 200
type arr4 = Maximum<[0, -1]> // 0
type arr5 = Maximum<[-1, 1]> // 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
本题是我多考虑了,官方没要求负数情况
如果要求是负数该如何实现呢?
优化后结果
点击查看答案
// 辅助类型
type NumToString<T extends number> = `${T}`
// 获取字符串长度
type GetStringLen<T extends string, U extends any[] = []> = T extends `${infer L}${infer R}` ? GetStringLen<R, [...U, L]> : U['length']
// 返回较小数字
type GetMinNumber<A extends number, B extends number, L extends any[] = []> = L['length'] extends A | B ? L['length'] extends A ? A : B : GetMinNumber<A, B, [...L, -1]>
// number is equal
type NumIsEqual<A extends number, B extends number> = A extends B ? true : false
// strlen is equal
type StrLenIsEqual<A extends string, B extends string> = GetStringLen<A> extends GetStringLen<B> ? true : false
// 返回较大值 A B 属于 0-9 A1 B1 为原始值
type GetMaxNumber<A extends number, B extends number, A1 extends number = A, B1 extends number = B, isAllNegative extends boolean = false,>
= A extends GetMinNumber<A, B> ? isAllNegative extends true ? A1 : B1 : isAllNegative extends true ? B1 : A1
// 判断是否是负数
type IsNegative<T extends number> = `${T}` extends `-${infer A extends number}` ? true : false
// 获取绝对值
type GetNegative<T extends number> = `${T}` extends `-${infer A extends number}` ? A : T
type Compare<
A extends number,
B extends number,
isAllNegative extends boolean = false,
A1 extends number = A,
B1 extends number = B,
S1 extends string = NumToString<A>,
S2 extends string = NumToString<B>
> =
A1 extends B1 ? A1:
StrLenIsEqual<S1, S2> extends true ?
(
S1 extends `${infer L1 extends number}${infer R1}`
? S2 extends `${infer L2 extends number}${infer R2}`
// 比较单个字符串对应数字情况
? NumIsEqual<L1, L2> extends true ?
// 相等递归下一次
Compare<A, B, isAllNegative, A1, B1, R1, R2> :
// 不相等话比较数字大小
GetMaxNumber<L1, L2, A1, B1, isAllNegative>
// 对比到此处说明 A1 === B1 所以任意返回一个值就行了
: never : never
)
:
(
// 转换成字符串后长度不相等
GetStringLen<S1> extends GetMinNumber<GetStringLen<S1>, GetStringLen<S2>> ?
// S1 长度小于 S2
isAllNegative extends false ? B1 : A1 :
// S1长度大于 S2
isAllNegative extends false ? A1 : B1
)
// 分为四种情况
// 1 A>= 0 B>=0 ===========> 比较 length ,谁的 length 大 返回谁
// 2 A>0 B<0 ===========> 直接返回 A
// 3 A<0 B>0 ===========> 直接返回 B
// 4 A<0 B<0 ========== > 谁的length小,值越大
type Max<A extends number, B extends number> =
IsNegative<A> extends true
? IsNegative<B> extends true
// A < 0 , B < 0
? Compare<GetNegative<A>, GetNegative<B>, true, A, B> : B
: IsNegative<B> extends true ? A : Compare<A, B>
// 答案
type Maximum<T extends number[], M extends number = T[0], First extends boolean = true> =
T['length'] extends 0 ? (
First extends true ? never : M
) :
T extends [infer A extends number, ...infer B extends number[]] ?
Maximum<B, Max<A, M>, false> : M
// test
type N1 = NumToString<100>
type N2 = NumToString<20>
type S1 = GetStringLen<N1>
// test GetMaxNumber
type G1 = GetMaxNumber<1, 2, 100, 200> // 200
type G2 = GetMaxNumber<1, 2, 100, 200> // 200
type G3 = GetMaxNumber<2, 1, -200, -100, true> // -100
type G4 = GetMaxNumber<1, 2, -100, -200, true> // -100
type G5 = GetMaxNumber<1, 1, 10, 10, false> // 10
type G6 = GetMaxNumber<1, 1, 10, 10, false> //
//test
type A1 = Max<30000000, 20000000> // 30000000
type A2 = Max<20000000, 30000000> // 30000000
type B1 = Max<0, 100000000> // 100000000
type B2 = Max<100000000, 0> // 100000000
type C1 = Max<-10000000000, -20000000000> // -10000000000
type C2 = Max<-20000000000, -10000000000> // -10000000000
type D1 = Max<-20000000, -10000000000000> // -20000000
type D2 = Max<-10000000000000, -20000000> // -20000000
type E1 = Max<-30000000, -30000000> // -20000000
type E2 = Max<-30000000, -30000000> // -20000000
// test
type arr0 = Maximum<[]> // never
type arr1 = Maximum<[-100000, -2000000, -2000000, -150000000]> // -100000
type arr2 = Maximum<[-10, -200000, -2000, -1500000000]> // -10
type arr3 = Maximum<[100000000, 20000, 20000, -150000]> // 100000000
type arr4 = Maximum<[0, -1]> // 0
type arr5 = Maximum<[-1, 1]> // 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
总结一下
- 均为正数
- 转换成字符串比较,谁的长度大,值就越大
- 同长度,将各个字符对应值,从左到右逐一比较,同位置字符对应数字越大,对应值越大,可以退出比较了
- 一正一负 直接返回正数
- 均为负数
- 转换成字符串,谁的长度小,值就越大
- 同长度,将各个字符对应值,从左到右逐一比较,同位置字符对应数字越小,对应值越大,可以退出比较了
为什么需要转换成字符串?
当数字值越大时,递归深度就越大,出现栈溢出。
转换成字符串有什么好处?
只比较长度,不会存在深度递归,同长度情况下,可以从左到右逐一比对,由于比较的是单个数字,这种情况下最大也是9,不会存在深度递归问题
// 未完待续……
# 地狱
# 🚀 实现 Equal
是不是很奇怪?一个简单 Equal
竟然放在地狱级别中,其实坑巨多
先看看最简单想到的方案
type MyEqual<A, B> = A extends B ? true : false
// test MyEqula
type M1 = MyEqual<1, 2> // false
type M2 = MyEqual<2, 1> // false
type M3 = MyEqual<1, 1> // true
type M4 = MyEqual<"a", "b"> // false
type M5 = MyEqual<"b", "a"> // false
type M6 = MyEqual<"a", "a"> // false
type M7 = MyEqual<any, 1> // boolean
type M8 = MyEqual<1, any> // true
type M9 = MyEqual<1, never> // false
type M10 = MyEqual<never, 1> // never
type M11 = MyEqual<never, any> // never
type M12 = MyEqual<any, never> // boolean
type M13 = MyEqual<never, never> // never
type M14 = MyEqual<{}, {}> // true
type M15 = MyEqual<[], []> // true
type M16 = MyEqual<{}, []> // false
type M17 = MyEqual<[], {}> // false
type M18 = MyEqual<[], any> // true
type M18_1 = MyEqual<any, []> // boolean
type M19 = MyEqual<any, {}> // boolean
type M20 = MyEqual<{}, any> // true
type M21 = MyEqual<any, any> // true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
哎这不对吧?为什么有的出现 never
有的出现 boolean
?
这就涉及到 ts 高级特性分发
改进方案
type MyEqual<A, B> = [A] extends [B] ? true : false
// ... 其他测试通过方案省略,下面演示不过的案例
type M7 = MyEqual<any, 1> // true ==============> 不符合期望 false
type M10 = MyEqual<never, 1> // true =============> 不符合期望 false
type M11 = MyEqual<never, any> // true =============> 不符合期望 false
type M18_1 = MyEqual<any, []> // true =============> 不符合期望 false
type M19 = MyEqual<any, {}> // true =============> 不符合期望 false
type M20 = MyEqual<{name:string}, {readonly name:string}> // true =============> 不符合期望 false
type M21 = MyEqual<{readonly name:string}, { name:string}> // true =============> 不符合期望 false
2
3
4
5
6
7
8
9
10
11
为了合理再改进
type MyEqual<A, B> = [A] extends [B] ? ([A] extends [B] ? true:false) : false
经过测试答案和上面一致,貌似这种方式也是错误的,有没有正确答案?
ts 官方是这样实现的
type MyEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false
为什么是这样,俺也不知道……,确实想不到, 暂时记着吧 😂
# 🚀 实现 slice
是不是觉得 slice 很简单,为什么是地狱级别?
题目描述
Implement the JavaScript Array.slice
function in the type system. Slice<Arr, Start, End>
takes the three argument. The output should be a subarray of Arr
from index Start
to End
. Indexes with negative numbers should be counted from reversely.
type Slice<
T extends any[],
Start extends number = 0,
End extends number = Arr['length'],
> =
// 处理入参后交由只处理合法数据的 DealSlice 处理
DealSlice<
T,
ConvertIndex<Start, T['length']>,
ConvertIndex<End, T['length']>
>;
// 本题核心实现
type DealSlice<
T extends any[],
Start extends number = 0,
End extends number = Arr['length'],
// 记录当前位置
Cur extends number[] = [],
// 标志位
flag = false,
> = GreaterThan<Start, End> extends false // 开始位置是否大于结束位置
? T extends [infer F, ...infer R]
? // 遍历元组,如果当前达到了起始位置
Cur['length'] extends Start
? // 同时也达到了结束位置,此时对应 start === end 的场景,返回空元组
Cur['length'] extends End
? []
: // 否则,将 元素加入新元组中,并在递归中将 flag 改为 true,表示在区间内
[F, ...DealSlice<R, Start, End, [...Cur, 1], true>]
: // 达到了结束位置
Cur['length'] extends End
? // 此时后续元素也不关心,直接返回空元组
[]
: // 不是起始也不是结束的场景,此时根据 flag 的情况,决定是否要把当前元素放入元组中
// 并将 flag 保留当前值继续后续遍历
flag extends true
? [F, ...DealSlice<R, Start, End, [...Cur, 1], flag>]
: DealSlice<R, Start, End, [...Cur, 1], flag>
: []
: []; // 开始位置大于结束位置,直接返回 []
// [4425-实现比较](/medium/4425-实现比较.md)
type GreaterThan<T extends number, U extends number, Arr extends any[] = []> =
// 先达到 T,则 T 小
T extends Arr['length']
? false
: // 先达到 U
U extends Arr['length']
? // 则 T 大
true
: // 都没到,膨胀元组
GreaterThan<T, U, [...Arr, 1]>;
// 构建长度为 Length 的元组
type ArrWithLength<Length extends number, Arr extends any[] = []> =
// 元组长度等于目标长度时
Arr['length'] extends Length
? // 返回元组
Arr
: // 否则,向 Arr 中增加一个元素,并递归处理新数组
ArrWithLength<Length, [...Arr, any]>;
// 减法实现: [进阶-计数-加减乘除](/summary/进阶-计数-加减乘除.md)
type Substract<A extends number, B extends number> = ArrWithLength<A> extends [
...ArrWithLength<B>,
...infer R,
]
? R['length']
: never;
// 处理负数入参
// 如果是负数 -n,返回 length - n, length - n > 0, 那么返回 0
// 如果是正数 n,返回 n
type ConvertIndex<
Index extends number,
Length extends number,
> = `${Index}` extends `-${infer F extends number}`
? GreaterThan<Length, F> extends true
? Substract<Length, F>
: 0
: Index;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# 🚀 获取只读字段
题目描述
实现泛型GetReadonlyKeys<T>
,GetReadonlyKeys<T>
返回由对象 T 所有只读属性的键组成的联合类型。
interface Todo {
readonly title: string
readonly description: string
completed: boolean
}
type Keys = GetReadonlyKeys<Todo> // expected to be "title" | "description"
2
3
4
5
6
7
如何获取 `readonly` 是个难度,可以从 `pick` 和 `Equal` 入手,主要难的想到使用 `Equal`以及 `Equal`实现
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false;
type GetReadonlyKeys<
T,
U extends Readonly<T> = Readonly<T>,
K extends keyof T = keyof T
> = K extends keyof T ? Equal<Pick<T, K>, Pick<U, K>> extends true ? K : never : never;
2
3
4
5
6
7
# 🚀 实现柯理化2
嗯……,柯里化不是实现了,为什么还会出现在这里?
困难级别中是固定参数个数,现在是动态参数了。
题目描述 在前端的日常开发中,柯里化函数参数个数动态化却是非常常见的
const func = (a: number, b: number, c: number) => {
return a + b + c
}
const bindFunc = func(null, 1, 2)
const result = bindFunc(3) // result: 6
2
3
4
5
6
7
因此,在 柯里化
的基础上,我们更需要的是 动态参数化的柯里化函数
const add = (a: number, b: number, c: number) => a + b + c
const three = add(1, 1, 1)
const curriedAdd = DynamicParamsCurrying(add)
const six = curriedAdd(1, 2, 3)
const seven = curriedAdd(1, 2)(4)
const eight = curriedAdd(2)(3)(4)
2
3
4
5
6
7
要求
传递给 DynamicParamsCurrying
的函数可能有多个参数,您需要实现它的类型。
在此挑战中,curriedAdd函数每次可接受最少一个参数,但是所有参数个数总和及类型与原函数相同。分配完所有参数后,它应返回其结果。
declare function DynamicParamsCurrying<A extends any[], R>(
fn: (...args: A) => R,
): A extends []
? R
: // 返回一个函数,其入参 P 通过在运行时进行推导
<P extends any[]>(
...args: P
) => A extends [...P, ...infer Rest]
? Rest extends []
? R
: // 对剩余的元素,递归处理,借助了 typeof, ReturnType 的内置类型
ReturnType<typeof DynamicParamsCurrying<Rest, R>>
: never;
2
3
4
5
6
7
8
9
10
11
12
13
# 🚀 实现 QueryStringParser
题目描述
You're required to implement a type-level parser to parse URL query string into a object literal type.
Some detailed requirements:
- Value of a key in query string can be ignored but still be parsed to
true
. For example,'key'
is without value, so the parser result is{ key: true }
. - Duplicated keys must be merged into one. If there are different values with the same key, values must be merged into a tuple type.
- When a key has only one value, that value can't be wrapped into a tuple type.
- If values with the same key appear more than once, it must be treated as once. For example,
key=value&key=value
must be treated askey=value
only.
本题是解析 query,要求是几个点:
k1
需要被解释为{ K1: true }
- 多次出现的且值不同,需要被改成元组类型,也就是
K1=1&K1=2&K1=3
,被解释为{ K1: [1, 2, 3] }
- 多次出现的相同值,不会被处理。
分析
如何提取 K=V => {K:V}
如何提取 &K =>{K:true}
如何将 K=V1&K=V2 =>{K:[V1,V2]}
如何将提取结果保存
type SingleProp<T extends string> =
T extends `${infer K}=${infer V}`?
{
[P in K]: V
}
:
{
[P in T as P extends '' ? never : P]: true
}
// test
type S1=SingleProp<"K=V"> // {K:V}
type S2=SingleProp<"K"> // {K:true}
// 判断 T 中是否包含 U
type Includes<T, U> = T extends [infer F, ...infer R]
? F extends U
? true
: Includes<R, U>
: false;
// 合并同时存在的属性值
type MergeSingle<U1, U2> =
// 如果 是元组
U1 extends any[]
? // 判断是否包含 U2
Includes<U1, U2> extends true
? // 包含,则不纳入
U1
: // 否则,纳入
[...U1, U2]
: // 不是元组,判断当前值是否相同
U1 extends U2
? // 相同,则不组成元组,直接返回 U1
U1
: // 否则,组成元组
[U1, U2];
// 完整的合并操作
type Merge<T1, T2> = {
// 遍历所有属性
[P in keyof T1 | keyof T2]: P extends keyof T1
? P extends keyof T2
? // 如果是两者都有的属性,调用上述的 MergeSingle
MergeSingle<T1[P], T2[P]>
: // 否则返回前者
T1[P]
: P extends keyof T2
? // 否则返回后者
T2[P]
: never;
};
type ParseQueryString<T extends string, Res = {}> =
// 根据 & 匹配前后字符
T extends `${infer F}&${infer R}`
? ParseQueryString<
R,
// 把 F 解析后的结果合并到 Res 中
Merge<Res, SingleProp<F>>
>
: // 匹配结束,处理 T 之后合并到 Res 中并返回便是答案
Merge<Res, SingleProp<T>>;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
拓展
相同key多次出现相同值,不合并,只保留第一次的value
# 🚀 实现 JSON Parse ❓
You're required to implement a type-level partly parser to parse JSON string into a object literal type.
Requirements:
Numbers
andUnicode escape (\uxxxx)
in JSON can be ignored. You needn't to parse them.
//My answer is only for the test cases
type Parse<T extends string> = Eval<T> extends [infer V, infer U] ? V : never
type Eval<T>
= T extends `${' '|'\n'}${infer U}` ? Eval<U>
: T extends `true${infer U}` ? [true, U]
: T extends `false${infer U}` ? [false, U]
: T extends `null${infer U}` ? [null, U]
: T extends `"${infer U}` ? EvalString<U>
: T extends `${'['}${infer U}` ? EvalArray<U>
: T extends `${'{'}${infer U}` ? EvalObject<U>
: false
type Escapes = {r:'\r', n:'\n', b:'\b', f:'\f'}
type EvalString<T, S extends string = ''>
= T extends `"${infer U}` ? [S, U]
: (T extends `\\${infer C}${infer U}` ? C extends keyof Escapes ? [C, U] : false : false) extends [infer C, infer U]
? EvalString<U, `${S}${C extends keyof Escapes ? Escapes[C] : never}`>
: T extends `${infer C}${infer U}` ? EvalString<U, `${S}${C}`>
: false
type EvalArray<T, A extends any[] = []>
= T extends `${' '|'\n'}${infer U}` ? EvalArray<U, A>
: T extends `]${infer U}` ? [A, U]
: T extends `,${infer U}` ? EvalArray<U, A>
: Eval<T> extends [infer V, infer U] ? EvalArray<U, [...A, V]>
: false
type EvalObject<T, K extends string = '', O = {}>
= T extends `${' '|'\n'}${infer U}` ? EvalObject<U, K, O>
: T extends `}${infer U}` ? [O, U]
: T extends `,${infer U}` ? EvalObject<U, K, O>
: T extends `"${infer U}` ? Eval<`"${U}`> extends [`${infer KK}`, infer UU] ? EvalObject<UU, KK, O> : false
: T extends `:${infer U}` ? Eval<U> extends [infer V, infer UU] ? EvalObject<UU, '', Merge<{[P in K]: V} & O>> : false
: false
type Merge<T> = {[P in keyof T]: T[P]}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 未完待续……
# 总结
// 未完待续……