TS试炼-6
摘要:
包含:ObjectEntries、Shift、Tuple to Nested Object(元组转嵌套对象)、Reverse(元组翻转)、Flip Arguments(函数参数翻转)、FlattenDepth(对元组扁平指定层级)、BEM(CSS流行命名约束)、InorderTraversal(二叉树索引遍历)、Flip(key和value交换)、Fibonacci(斐波那契序列)
1、ObjectEntries(Object.entries())
ts
interface Model {
name: string;
age: number;
locations: string[] | null;
}
type modelEntries = ObjectEntries<Model> // ['name', string] | ['age', number] | ['locations', string[] | null];
第一种方式
ts
type RemoveUndefined<T> = [T] extends [undefined] ? T : Exclude<T, undefined>;
type ObjectEntries<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? [K, RemoveUndefined<T[K]>] : [K, T[K]];
}[keyof T];
解析:
通过{} extends Pick<T, K>
判断属性是否可选,根据是否可选处理要不要去除undefined
第二种方式
ts
type ObjectEntries<T, U = Required<T>> = {
[K in keyof U]: [K, U[K] extends never ? undefined : U[K]]
}[keyof U]
解析:
使用第二个变量,值为第一个参数的属性必选,必选后有以下情况
1.如果原属性是必选,则结果还为原类型(包括undefined)
2.如果原属性可选,则去除undefined,去除后如果没有类型则为never
如此:就执行要处理一下never的情况即可满足试题要求。
第三种方式
ts
type ObjectEntries<T extends object, K extends keyof T = keyof T> = K extends K ? [K, [T[K]] extends [undefined] ? T[K] : Required<T>[K]] : never
type ObjectEntries<T extends object, U = Required<T>, K extends keyof U = keyof U> = K extends K ? [K, [U[K]] extends [never] ? undefined : U[K]] : never
解析:
先把key拿出来,key为联合类型,对联合类型执行extends相当于遍历。
仍然使用Required,只需要处理一下只为undefined的情况即可
2、Shift(删除元组第一个类型)
ts
type Result = Shift<[3, 2, 1]> // [2, 1]
实现
ts
type Shift<T extends unknown[]> = T extends [unknown, ...infer R] ? R : []
3、Tuple to Nested Object(元组转嵌套对象)
ts
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
实现
ts
type TupleToNestedObject<T extends unknown[], U> = T extends [infer F extends PropertyKey, ...infer R] ? {
[key in F]: TupleToNestedObject<R, U>
} : U
4、Reverse (元组翻转)
ts
type a = Reverse<['a', 'b']> // ['b', 'a']
type b = Reverse<['a', 'b', 'c']> // ['c', 'b', 'a']
实现
ts
type Reverse<T extends unknown[]> = T extends [infer F, ...infer R] ? [...Reverse<R>, F] : T
type Reverse<T extends unknown[]> = T extends [...infer R, infer L] ? [L, ...Reverse<R>] : T
5、Flip Arguments(函数参数翻转)
ts
type Flipped = FlipArguments<(arg0: string, arg1: number, arg2: boolean) => void>
// (arg0: boolean, arg1: number, arg2: string) => void
实现
ts
type Reverse<T extends unknown[]> = T extends [infer F, ...infer R] ? [...Reverse<R>, F] : T
type FlipArguments<T extends Function> = T extends (...args: infer A) => infer R ? (...args: Reverse<A>) => R : never
// 也可以合并成一个
type FlipArguments<T extends Function, Args extends unknown[] = []> = T extends (
...args: infer A
) => infer V
? A extends [infer F, ...infer R]
? FlipArguments<(...args: R) => V, [F, ...Args]>
: (...args: Args) => V
: never;
解析:
主要是通过infer推断出参数,然后使用上面的元组翻转工具即可。
6、FlattenDepth(对元组扁平指定层级)
ts
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
实现
ts
// 扁平化一层
type FlattenArray<T extends unknown[]> = T extends [infer F, ...infer R]
? F extends unknown[]
? [...F, ...FlattenArray<R>]
: [F, ...FlattenArray<R>]
: T;
// 判断是否已经完全扁平
type IsFlattenArray<T extends unknown[]> = T extends [infer F, ...infer R]
? F extends unknown[]
? false
: IsFlattenArray<R>
: true;
// 扁平指定层级
type FlattenDepth<T extends unknown[], D extends number = 1> = D extends 0 // 次数完毕
? T
: IsFlattenArray<T> extends true // 已经完全扁平,则直接结束,避免无限递归
? T
: FlattenDepth<FlattenArray<T>, MinusOne<D>>; // 每次执行减一
解析:
通过判断层级为0或者已经完全扁平结束,未结束就将层级减一继续。MinusOne
工具参考上一篇:MinusOne
另一种方案
ts
// IsFlattenArray、FlattenArray同上
type FlattenDepth<
T extends unknown[],
D extends number = 1,
U extends unknown[] = [],
> = U['length'] extends D
? T
: IsFlattenArray<T> extends true
? T
: FlattenDepth<FlattenArray<T>, D, [...U, 0]>;
解析:
每执行一次往元组里添加一个值,通过元组长度判断是否结束。(第一种占时间,第二种占空间)
注意:只要是使用递归处理的,都会有一个问题:递归次数达到限制,便会异常。
比较简洁的方案
ts
// FlattenArray同上,是否完全扁平也用此方法判断
type FlattenDepth<
T extends any[],
D extends number = 1,
U extends any[] = []
> = U['length'] extends D ? T : (
FlattenArray<T> extends T ? T : ( // 如果扁平一层和不扁平相同,则直接返回
FlattenDepth<FlattenArray<T>, D, [...U, 0]>
)
)
7、BEM(CSS流行命名约束)
ts
BEM<'btn', ['price'], []> -> 'btn__price'
BEM<'btn', ['price'], ['warning', 'success']> -> 'btn__price--warning' | 'btn__price--success'
BEM<'btn', [], ['small', 'medium', 'large']> -> 'btn--small' | 'btn--medium' | 'btn--large'
实现
ts
type BEM<B extends string, E extends string[], M extends string[]> = `${B}${E extends [] ? '' : `__${E[number]}`}${M extends [] ? '' : `--${M[number]}`}`
8、InorderTraversal(二叉树索引遍历)
ts
const tree1 = {
val: 1,
left: null,
right: {
val: 2,
left: {
val: 3,
left: null,
right: null,
},
right: null,
},
} as const
// 结果:[1,3,2]
实现
ts
interface TreeNode {
val: number
left: TreeNode | null
right: TreeNode | null
}
type InorderTraversal<T extends TreeNode | null> = T extends TreeNode ? [...InorderTraversal<T['left']>, T['val'], ...InorderTraversal<T['right']>] : [];
9、Flip(key和value交换)
ts
Flip<{ a: "x", b: "y", c: "z" }>; // {x: 'a', y: 'b', z: 'c'}
Flip<{ a: 1, b: 2, c: 3 }>; // {1: 'a', 2: 'b', 3: 'c'}
Flip<{ a: false, b: true }>; // {false: 'a', true: 'b'}
实现
ts
type Flip<T extends Record<string, string | number | boolean>> = {
[P in keyof T as `${T[P]}`]: P;
};
解释:
通过[P in keyof T]
拿到key,用于新类型的值,再通过as来指定新类型的key类型。
10、Fibonacci(斐波那契序列)
ts
type Result1 = Fibonacci<3> // 2
type Result2 = Fibonacci<8> // 21
实现
ts
type Fibonacci<
T extends number,
No extends 0[] = [0, 0, 0],
N_2 extends 0[] = [0],
N_1 extends 0[] = [0],
> = T extends 1 | 2
? 1
: T extends No['length']
? [...N_1, ...N_2]['length']
: Fibonacci<T, [...No, 0], N_1, [...N_1, ...N_2]>;
解释:
使用数组和length实现
1、No用于存储当前是第几次,第一次和第二次比较特殊,结果都是1,可以特殊处理。T extends No['length']
是从3开始判断。因此NO的初始值是一个长度为3的元组,,n1和n2都是长度为1的元组,这样初始值固定。
2、当T extends No['length']
满足时,意味符合传入的参数,可以返回结果了,结果为n1.length+n2.length
3、不符合时,就需要递归了,第一个参数T不变,继续传递,No因为要执行下一次,所以把数组长度加一即可,下次的n1使用本次的n1和n2合并,下次的n2使用本次的n1。
注意:因ts元组最大长度限制,超出会提示元组过大,所以此方法最多支持的20,21时会报错。
支持更多的方法:🤣
ts
// let arr = [1,1]
// for(let i = 1; i < 51; i++) arr.push(arr[i] + arr[i-1]) // 元组生成方法
// console.log(arr)
type FibonacciTuple = [
0, 1, 1, 2,
3, 5, 8, 13,
21, 34, 55, 89,
144, 233, 377, 610,
987, 1597, 2584, 4181,
6765, 10946, 17711, 28657,
46368, 75025, 121393, 196418,
317811, 514229, 832040, 1346269,
2178309, 3524578, 5702887, 9227465,
14930352, 24157817, 39088169, 63245986,
102334155, 165580141, 267914296, 433494437,
701408733, 1134903170, 1836311903, 2971215073,
4807526976, 7778742049, 12586269025, 20365011074
]
type Fibonacci<T extends number> = FibonacciTuple[T];
评论
0条评论
暂无内容,去看看其他的吧~