TS进阶题目
- 在线 TS 地址 typescript
第一题
- 以下代码为什么会提示错误,应该如何解决上述问题?
1 | type User = { |
第一种解决方案
1 | type User = { |
第二种解决方案
1 | type User = { |
第二题
- 以下函数我们希望参数a和b的类型都是一致的,即a和b同时为number或string类型,当它们的类型不一致时,ts 类型检查器能自动提示对应的错误信息。
1 | function f(a: string | number,b: string | number) { |
第一种解决方案
- 函数重载
1 | function f(a: string, b: string): string; |
第二种解决方案
- 自定义路由守卫
1 | const isStringArray = (params: string[] | number[]): params is string[] => typeof params[0] === 'string'; |
第三种解决方案
- 范型
1 | function f<T extends string | number>(a: T, b: T) { |
第三题
- 实现 SetOptional 工具类型,支持把给定的 keys 对应的属性变为可选,参考Partial
- 实现 SetRequired 工具类型,支持把给定的 keys 对应的属性变成必填,参考 Require
1 | // SetOptional 测试用例 |
解决方案 1
- SetOptional
1 | type Foo = { |
- SetRequired
1 | type Foo = { |
解决方案 2
1 | type Foo = { |
解决方案 3
1 | type Simplify<T> = { |
第四题
- Pick<T, K extends keyof T> 的作用是将某个类型中的字属性挑出来,得到包含这个类型部分属性的字类型。
1 | interface Todo { |
- 问题:如何定义一个ConditionalPick工具类型,支持根据指定的Condition条件来生成新的类型,对应的使用示例如下:
1 | interface Example { |
解决方案
1 | interface Example { |
第五题
- 定义一个工具类型AppendArgument,为已有函数累心增加指定类型的参数,新增的参数名是x,将作为新函数类型的第一个参数,示例如下:
1 | type Fn = (a: number, b: string) => number; |
解决方案 1
1 | type AppendArgument<F extends (...args: any) => any, A> = (x: A, ...args: Parameters<F>) => ReturnType<F> |
解决方案 2
- 延伸阅读 用上这几招,轻松实现 TS 类型提取
1 | type AppendArgument<F, T> = F extends (...args: infer Args) => infer Return ? (x: T, ...args: Args) => Return : never; |
第六题
- 定义一个 NativeFlat 工具类型,支持把数组类型拍平(扁平化),示例如下:
1 | type NativeFlat<T extends any[]> = // 实现代码 |
- 在完成NativeFlat工具类型之后,继续实现DeepFlat工具类型,以支持多维数组类型:
1 | type DeepFlat<T extends any[]> = unknown // 你的实现代码 |
解决方案
- NativeFlat
1 | type NaiveFlat<T extends any[]> = { |
- DeepFlat
1 | type Deep = [['a'], ['b', 'c'], [['d']], [[[['e']]]]]; |
第七题
- 使用类型别名定义一个EmptyObject类型,是的该类型只允许空对象赋值:
1 | type EmptyObject = {} |
- 更改以下takeSomeTypeOnly函数类型定义,让参数只允许严格 SomeType 类型的值,示例如下:
1 | type SomeType = { |
解决方案
- EmptyObject
1 | // type PropertyKey = string | number | symbol; |
- takeSomeTypeOnly
1 | type SomeType = { |
第八题
- 定义一个NonEmptyArray工具类型,用于确保数据为非空数组
1 | type NonEmptyArray<T> = // 代码实现 |
解决方案 1
1 | type NonEmptyArray<T> = [T, ...T[]]; |
解决方案 2
1 | type NonEmptyArray<T> = T[] & {0: T}; |
解决方案 3
1 | type NonEmptyArray<T> = { |
第九题
- 定义一个JoinStrArray工具类型,用于根据指定的
Separator
分隔符,对字符串数据类型进行拼接,示例如下:
1 | type JoinStrArray<Arr extends string[], Separator extends string, Result extends string = ""> = // 你的实现代码 |
解决方案
1 | type JoinStrArray< |
第十题
- 实现一个Trim工具类型,用于对字符串字面量类型进行去空格处理,示例如下:
1 | type Trim<V extends string> = // 代码实现 |
解决方案
1 | type TrimLeft<V extends string> = V extends ` ${infer R}` ? TrimLeft<R> : V; |
第十一题
- 实现一个*IsEqual<A, B>*工具类型,用于比较两个类型是否相等,示例如下:
1 | type IsEqual<A, B> = // 代码实现 |
解决方案(原始方案) 1
1 | type IsEqual<A, B> = A extends B ? (B extends A ? true : false) : false; |
解决方案(稍微优化)2
1 | type IsEqual<A, B> = [A] extends [B] ? [B] extends [A] ? true : false : false |
解决方案 4
1 | type IsEqual<A, B> = |
第十二题
- 实现一个Head工具类型,用于获取数组类型的第一个类型,示例如下:
1 | type Head<T extends Array<any>> = // 代码实现 |
解决方案 1
1 | type Head<T extends Array<any>> = T extends [] ? never : T[0]; |
解决方案 2
1 | type Head<T extends Array<any>> = T extends [head: infer H, ...rest: any[]] ? H : never; |
第十三题
- 实现一个Tail工具类型,用于获取数组类型除了第一个类型外,剩余的类型,示例如下:
1 | type Tail<T extends Array<any>> = // 代码实现 |
解决方案
1 | type Tail<T extends Array<any>> = T extends [infer A, ...infer B] ? B : []; |
第十四题
- 实现一个Unshift工具类型,用于把指定类型 E 作为第一个元素添加到T数组类型中,示例如下:
1 | type Unshift<T extends any[], E> = // 代码实现 |
解决方案
1 | type Unshift<T extends any[], E> = [E, ...T]; |
第十五题
- 实现一个Shift工具类型,用于移除T数组类型中的第一个类型,示例如下:
1 | type Shift<T extends any[]> = // 代码实现 |
解决方案
1 | type Shift<T extends any[]> = T extends [infer A, ...infer B] ? B : []; |
第十六题
- 实现一个Push工具类型,用于把指定类型E作为最后一个元素添加到T数组类型中,示例如下:
1 | type Push<T extends any[], E> = // 代码实现 |
解决方案 1
1 | type Push<T extends any[], E> = T extends [...infer U] ? [...U, E] : never; |
解决方案 2
1 | type Push<T extends any[], E> = [...T, E]; |
第十七题
- 实现一个Includes工具类型,用于判断指定的类型E,是否包含在T数组类型中,示例如下:
1 | type Includes<T extends any[], E> = // 代码实现 |
解决方案 1
1 | type Includes<T extends any[], E> = E extends T[number] ? true : false; |
解决方案 2
1 | type Includes<T extends any[], E> = T extends [infer A, ...infer R] ? E extends A ? true : Includes<R, E> : false; |
第十八题
- 实现一个UnionToIntersection工具类型,用于把联合类型转换为交叉类型,示例如下:
1 | type UnionToIntersection<U> = // 代码实现 |
解决方案
注释
1 | type Fun<X> = (...args: X[]) => void; |
1 | type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never |
第十九题
- 实现一个 OptionalKeys 工具类型,用来获取对象类型中声明的可选属性,示例如下:
1 | type Person = { |
解决方案 1
1 | type Person = { |
解决方案 2
1 | type Person = { |
解决方案 3
1 | type Person = { |
解决方案 4
1 | type Person = { |
第二十题
- 实现一个Curry工具类型,用来实现函数类型的柯里化处理,示例如下:
1 | type Curry< |
解决方案
1 | type Curry< |
第二十一题
- 实现一个Merge工具类型,用于把两个类型合成一个新的类型,第二类型(SecondType)的Keys将会覆盖第一种类型(FirstType)的Keys,示例如下:
1 | type Foo = { |
解决方案 1
- 将 FirstType 和 SecondType 做交叉类型,并遍历每一个属性;
- 如果当前属性名在 SecondType 类型中,则使用 SecondType 类型中的当前属性值;
- 如果当前属性名在 FirstType 类型中,则使用 FirstType 类型中的当前属性值;
- 否则为 never;
1 | type Foo = { |
解决方案 2
- 先将 FirstType 类型中已经有的,和 SecondType 类型中相同的属性删除;
- 将前面结果和 SecondType 做交叉类型,获得合并后结果。
1 | type Foo = { |
第二十二题
- 实现一个RequireAtLeastOne工具类型,它将创建一个至少含有一个给定keys的类型,其余keys类型保持原样,示例如下:
1 | type Responder = { |
解决方案 1
1 | type Responder = { |
解决方案 2
1 | type Responder = { |
解决方案 3
1 | type Responder = { |
第二十三题
- 实现一个RemoveIndexSignature工具类型,用于移除已有类型中的索引签名,示例如下:
1 | interface Foo { |
解决方案
1 | interface Foo { |
第二十四题
- 实现一个
Mutable
工具类型,用于移除对象类型上所有属性或者部分属性的readonly
修饰符,示例如下:
1 | type Foo = { |
解决方案 1
1 | type Foo = { |
解决方案 2
1 | type Foo = { |
第二十五题
- 实现一个
IsUnion
工具类型,判断指定的类型是否为联合类型,示例如下:
1 | type IsUnion<T, U = T> = // 代码实现 |
解决方案
1 | type IsUnion<T, U = T> = T extends any ? [U] extends [T] ? false : true : never; |
知识点: 1.联合类型作为泛型的时候 extends 会触发分发执行 2.联合类型 T 写成[T]就变成了普通类型,extends 的时候不会分发执行
这里第一步T extends any
肯定为真,一个其实就是利用其分发的特性,后面的[T]就是一个联合类型拆开后的某一个,因此如果是联合类型的话[U] extends [T]
一定为否
第二十六题
- 实现一个
IsNever
工具类型,判断指定的类型是否为never
类型,示例如下:
1 | type IsNever<T> = // 代码实现 |
解决方案
- 用
[]
包裹 T,否则泛型参数会被当作一个裸类型处理,走条件式分布类型
的判断逻辑,当泛型参数是 any 这种特殊值时,会得到分布后的类型。
1 | type IsNever<T> = [T] extends [never] ? true : false; |
第二十七题
- 实现一个
Reverse
工具类型,用于对元祖类型中元素的位置颠倒,并返回该数组,元祖的第一个元素就会变成最后一个,最后一个元素变成第一个。
1 | type Reverse< |
解决方案 1
1 | type Reverse< |
解决方案 2
1 | type Reverse< |
第二十八题
- 实现一个
Split
工具类型,根据给定的分割符(Delimiter)对包含分割符的字符串进行切割,可用于定义String.prototype.split
方法的返回值类型,示例如下:
1 | type Item = `zs, ls, ww`; |
解决方案
1 | type Item = `zs, ls, ww`; |
第二十九题
- 实现一个
ToPath
工具类型,用于把属性访问(.或[])
路径转换为元祖的形式,示例如下:
1 | type ToPath<S extends string> = // 代码实现 |
解决方案
1 | type ToPath<S extends string> = S extends `${infer F}${`[${infer D}]`}${infer R}` |
第三十题
- 完善
Chainable
类型的定义,是的 TS 能成功推断出result
变量的类型,调用option
方法之后会不断扩展当前对象的类型,使得调用get
方法后能获取正确的类型。
1 | declare const config: Chainable |
解决方案
1 | declare const config: Chainable; |
第三十一题
- 实现一个
Repeat
工具类型,用于根据类型变量C
的值,重复T
类型并以元祖的形式返回新的类型,示例如下:
1 | type Repeat<T, C extends number> = // 代码实现 |
解决方案
1 | type Repeat<T, C extends number, A extends any[]> = A["length"] extends C ? A : Repeat<T, C, [...A, T]>; |
第三十二题
- 实现一个
RepeatString
工具类型,用于根据类型变量C
的值,重复 T 类型并以字符串
的形式返回新的类型,示例如下:
1 | type RepeatString< |
解决方案
1 | type RepeatString< |
第三十三题
- 实现一个
ToNumber
工具类型,用于实现把数值字符串转换为数值类型,示例如下:
1 | type ToNumber<T extends string> = // 代码实现 |
解决方案
1 | type ToNumber<T extends string, S extends any[] = [], L extends number = S["length"]> = |
第三十四题
- 实现一个
SmallerThan
工具类型,用于比较数值类型的大小,示例如下:
1 | type SmallerThan< |
解决方案
1 | type SmallerThan< |
第三十五题
- 实现一个
Add
工具类型,用于实现对数组对应的数值进行加法运算,示例如下:
1 | type Add<T, R> = // 代码实现 |
解决方案
1 | type Push<T extends number[], V> = [...T, V]; |
第三十六题
- 实现一个
Filter
工具类型,用于根据类型变量F
的值进行类型过滤,示例如下:
1 | type Filter<T extends any[], F> = // 代码实现 |
解决方案
1 | // 本题关键点就是对`any`类型的处理: |
1 | type IsAny<T> = 0 extends (1 & T) ? true : false; |
第三十七题
- 实现一个
Flat
工具类型,支持把数组类型拍平(扁平化),示例如下:
1 | type Flat<T extends any[]> = // 代码实现 |
解决方案 1
1 | type Flat<T extends any[]> = T extends [infer First, ...infer Rest] |
解决方案 2
1 | type Flat<T extends any[], S extends any[] = []> = T extends [infer R, ...infer Rest] |
第三十八题
- 实现
StartsWith
工具类型,判断字符串字面量类型T
是否以给定的字符串字面量类型U
开头,并根据判断结果返回布尔值,示例如下:
1 | type StartsWith<T extends string, U extends string> = // 代码实现 |
- 之后,继续实现
EndsWith
工具类型,判断字符串字面量类型T
是否以给定的字符串字面量类型U
结尾,并根据判断结果返回布尔值,示例如下:
1 | type EndsWith<T extends string, U extends string> = // 代码实现 |
解决方案
1 | // StartWith |
第三十九题
- 实现
IsAny
工具类型,用于判断类型T
是否为any
类型,示例如下:
1 | type IsAny<T> = // 代码实现 |
解决方案 1
1 | // 思路: 利用任何类型和any交叉都等于any来实现 |
解决方案 2
1 | // unknown 只能赋给 unknown 或者 any |
第四十题
- 实现
AnyOf
工具类型,只要数组中任意元素的类型非Falsy
类型、{}
类型或[]
类型,则返回true
,否则返回false
,如果数组为空的话,则返回false
,示例如下:
1 | type AnyOf<T extends any[]> = // 代码实现 |
解决方案
1 | type Falsy = { |
第四十一题
- 实现
Replace
工具类型,用于实现字符串类型的替换操作,具体的使用示例如下:
1 | type Replace< |
- 此外,继续实现
ReplaceAll
工具类型,用于实现替换所有满足条件的字串,示例如下:
1 | type ReplaceAll< |
解决方案
1 | type Replace< |
1 | type ReplaceAll< |
第四十二题
- 实现
IndexOf
工具类型,用于获取数组类型中指定项的索引值,若不存在的话,则返回-1
字面量类型,示例如下:
1 | type IndexOf<A extends any[], Item> = // 代码实现 |
解决方案
1 | type IndexOf<A extends any[], Item, R extends any[] = []> = A extends [infer H, ...infer Rest] |
第四十三题
- 实现一个
Permutation
工具类型,当输入一个联合类型时,返回一个包含该联合类型的全排列类型数组。示例如下:
1 | type Permutation<T, K = T> = // 代码实现 |
解决方案
1 | type Permutation<T, K = T> = [T] extends [never] |
第四十四题
- 实现
Unpacked
工具类型,用于对类型执行“拆箱”操作,示例如下:
1 | type Unpacked<T> = // 代码实现 |
解决方案
1 | type Unpacked<T> = T extends (...args: any) => infer A |
第四十五题
- 实现
JsonifiedObject
工具类型,用于对Object
对象类型进行序列话操作,示例如下:
1 | declare class MyClass { |
解决方案
1 | declare class MyClass { |
第四十六题
- 实现
RequireAllOrNone
工具类型,用于满足以下功能,当设置age
属性时,gender
属性也会变成必填,示例如下:
1 | interface Person { |
解决方案
1 | interface Person { |
第四十七题
- 实现
RequireExactlyOne
工具类型,用于满足以下功能,即只能包含age
或gender
属性,不能包含着两个属性,示例如下:
1 | interface Person { |
解决方案
1 | interface Person { |
第四十八题
- 实现
ConsistsOnlyOf
工具类型,用于判断LongString
字符串类型是否由 0 个或多个Substring
字符串类型组成,示例如下:
1 | type ConsistsOnlyOf<LongString extends string, SubString extends string> = // 代码实现 |
解决方案
1 | type ConsistsOnlyOf<LongString extends string, SubString extends string> = |
第四十九题
- 项目中定义了接口返回的数据的类型,每层都能灵活扩展一些属性,怎么做呢?
1 | // 接口返回数据结构 |
解决方案
1 | type DeepRecord<Obj extends Record<keyof any, unknown>> = { |
第五十题
- 当一个索引为 ‘desc’ | ‘asc’ 的时候,其他索引都是 false
解决方案
1 | type GenerateType<keys extends keyof any, V1, V2> = { |
第五十一题
- 取出interface中userInfo的类型
1 | interface Result { |
解决方案 1
1 | // 简单版 Required |
解决方案 2
1 | // 递归Required |