Type Challenges -- TS 练习题之 Medium 篇答案 (持续更新)

747 阅读7分钟

Type Challenges -- TS 练习题之 Medium 篇答案 (持续更新)

感谢 type-challenges 提供的题目

前言

点击查看题目

建议 fork 原仓库地址在本地进行答题
原仓库地址:type-challenges
答案仓库地址: github.com/fortress-fi…

相关文章

Type Challenges -- TS 练习题之 Easy 篇答案
Type Challenges -- TS 练习题之 Medium 篇答案 (持续更新)
Type Challenges -- TS 练习题之 Hard 篇答案 (持续更新)

medium


00002-medium-return-type

type MyReturnType<T> = T extends (...args: any[]) => infer P ? P : never

00003-medium-omit

type MyOmit<T, K extends keyof T> = {
  [P in MyExclude<keyof T, K>]: T[P]
}

00008-medium-readonly-2

type MyReadonly2<T, K extends keyof T = keyof T> = {
  readonly [P in K]: T[P]
} & {
  [P in MyExclude<keyof T, K>]: T[P]
}

00009-medium-deep-readonly

type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends Record<string, unknown>
    ? T[P]
    : DeepReadonly<T[P]>
}

00010-medium-tuple-to-union

type TupleToUnion<T extends any[]> = T[number]

00012-medium-chainable-options [使用可串联(Chainable/Pipeline)的函数构造一个对象]

type Chainable<T extends Object = {}> = {
  option<K extends string, V>(
    key: K extends keyof T ? never : K,
    value: V,
  ): Chainable<T & { [P in K]: V }>
  get(): { [P in keyof T]: T[P] }
}

00459-medium-flatten [

00015-medium-last [实现一个通用Last<T>,它接受一个数组T并返回其最后一个元素的类型]

type Last<T extends any[]> = T extends [...any[], infer K] ? K : never

00016-medium-pop [实现一个通用Pop<T>,它接受一个数组T并返回一个没有最后一个元素的数组]

type Pop<T extends any[]> = T extends [...infer K, any] ? K : never

00020-medium-promise-all

declare function PromiseAll<T extends unknown[]>(
  values: readonly [...T],
): Promise<{ [P in keyof T]: T[P] extends Promise<infer K> ? K : T[P] }>

00062-medium-type-lookup [使用指定属性作为条件,筛选出联合属性中符合要求的那个]

type LookUp<U extends { type: string }, T> = U extends { type: T } ? U : never

00106-medium-trimleft [trimleft 方法的类型定义]

type TrimCharacter = ' ' | '\n' | '\t'

type TrimLeft<S extends string> = S extends `${TrimCharacter}${infer T}`
  ? TrimLeft<T>
  : S

00108-medium-trim [trim 方法的类型定义]

type TrimCharacter2 = ' ' | '\n' | '\t'

type Trim<S extends string> = S extends `${TrimCharacter2}${infer T}`
  ? Trim<T>
  : S extends `${infer T}${TrimCharacter2}`
    ? Trim<T>
    : S

00110-medium-capitalize [首字母大写]

type MyCapitalize<S extends string> = S extends `${infer T}${infer K}`
  ? `${Uppercase<T>}${K}`
  : ''

00116-medium-replace [replace 类型定义]

type Replace<
  S extends string,
  From extends string,
  To extends string,
> = S extends `${infer T}${From}${infer E}`
  ? `${T}${From extends '' ? '' : To}${E}`
  : S

00119-medium-replaceall [replaceAll 类型定义]

type ReplaceAll<
  S extends string,
  From extends string,
  To extends string,
> = S extends `${infer F}${From}${infer E}`
  ? From extends ''
    ? S
    : `${ReplaceAll<F, From, To>}${To}${ReplaceAll<E, From, To>}`
  : S

00191-medium-append-argument [追加参数类型]

type AppendArgument<Fn extends (...param: any[]) => any, A> = Fn extends (
  ...param: infer T
) => infer R
  ? (...param: [...T, A]) => R
  : never

00296-medium-permutation [实现联合类型的全排列,将联合类型转换成所有可能的全排列数组的联合类型]

// NOTE: 错题本

type Permutation<T, U = T> = [T] extends [never]
  ? []
  : T extends U
    ? [T, ...Permutation<Exclude<U, T>>]
    : []

此处可以开出 extends 在执行的过程中,对联合类型拆解后进行循环执行,并将循环后的结果组成了新的联合类型,示例:

type Test<T> = T extends T ? [T] : []
type result = Test<'a' | 'b'>
// type result = ["a"] | ["b"]

00298-medium-length-of-string [计算字符串的长度,类似于 String#length ]

type LengthOfString<
  S extends string,
  U extends any[] = [],
> = S extends `${infer F}${infer O}`
  ? LengthOfString<O, [...U, F]>
  : U['length']

00459-medium-flatten [数组 flatten 方法类型定义]

type Flatten<T extends any[], N extends any[] = []> = T extends [
  infer F,
  ...infer O,
]
  ? F extends any[]
    ? Flatten<O, [...N, ...Flatten<F>]>
    : Flatten<O, [...N, F]>
  : N

00527-medium-append-to-object

type AppendToObject<T, U extends string, V> = {
  [P in keyof T | U]: P extends keyof T ? T[P] : V
}

00529-medium-absolute [取绝对值]

type Absolute<T extends number | string | bigint> = `${T}` extends `-${infer U}`
  ? U
  : `${T}`

00531-medium-string-to-union [split string]

type StringToUnion<T extends string> = T extends `${infer S}${infer O}`
  ? S | StringToUnion<O>
  : never

00599-medium-merge [合并两个 Object 类型]

type Merge<F, S> = {
  [P in keyof F | keyof S]: P extends keyof S
    ? S[P]
    : P extends keyof F
      ? F[P]
      : never
}

00612-medium-kebabcase [kebabcase 格式转换]

type KebabCase<
  S extends string,
  B extends string = '',
> = S extends `${infer F}${infer O}`
  ? F extends Uppercase<F>
    ? F extends Lowercase<F>
      ? KebabCase<O, `${B}${F}`>
      : KebabCase<O, `${B}${B extends '' ? '' : '-'}${Lowercase<F>}`>
    : KebabCase<O, `${B}${F}`>
  : B

00645-medium-diff [获取两个接口类型中的差值属性]

type DiffKey<C, C1> = C extends C1 ? never : C

type Diff<O, O1> = {
  [P in DiffKey<keyof (O & O1), keyof (O | O1)>]: P extends keyof O
    ? O[P]
    : P extends keyof O1
      ? O1[P]
      : never
}

00949-medium-anyof [类型接收一个数组,如果数组中任一个元素为真,则返回 true,否则返回 fasle。如果数组为空,返回 false]

type AnyOf<T extends readonly any[]> = T extends [infer F, ...infer O]
  ? F extends 0 | '' | false | [] | Record<string, never>
    ? AnyOf<O>
    : true
  : false

表示一个空对象的方法:Record<string, never>

01042-medium-isnever [never 判断]

type IsNever<T> = [T] extends [never] ? true : false

如果直接使用 T 做判断,当 Tnever 的时候,将始终返回 never

01097-medium-isunion [联合类型判断]

type IsUnion<T, N = T> = T extends T ? ([N] extends [T] ? false : true) : never

01130-medium-replacekeys [替换值类型]

type ReplaceKeys<U, T, Y> = {
  [P in keyof U]: P extends T ? (P extends keyof Y ? Y[P] : never) : U[P]
}

01367-medium-remove-index-signature [移除索引]

type RemoveIndexSignature<T> = {
  [P in keyof T as P extends `${infer K}` ? K : never]: T[P]
}

这里的 as 作为对 P 的进一步定义使用

01978-medium-percentage-parser [分离正负,数字,单位]

type PercentageParser<
  A extends string,
  S extends string = '',
  N extends string = '',
  U extends string = '',
> = A extends `${infer F}${infer O}`
  ? F extends '+' | '-'
    ? PercentageParser<O, F, '', ''>
    : F extends '%'
      ? PercentageParser<O, S, N, F>
      : PercentageParser<O, S, `${N}${F}`, U>
  : [S, N, U]

02070-medium-drop-char [移除指定字符]

type DropChar<S, C, R extends string = ''> = S extends `${infer F}${infer O}`
  ? F extends C
    ? DropChar<O, C, `${R}`>
    : DropChar<O, C, `${R}${F}`>
  : R

02257-medium-minusone (未处理)

02595-medium-pickbytype [筛选对象中,值符合要求类型的键]

type PickByTypeFilter<
  T extends Record<string, any>,
  P extends keyof T,
  U,
> = T[P] extends U ? P : never
type PickByType<T, U> = {
  [P in keyof T as PickByTypeFilter<T, P, U>]: T[P]
}

02688-medium-startswith [接收两个string类型参数,然后判断T是否以U开头,根据结果返回truefalse]

type StartsWith<
  T extends string,
  U extends string,
  S extends string = '',
> = T extends `${infer F}${infer O}`
  ? U extends S
    ? true
    : StartsWith<O, U, `${S}${F}`>
  : false

02693-medium-endswith [接收两个string类型参数,然后判断T是否以U结尾,根据结果返回truefalse]

type EndsWith<
  T extends string,
  U extends string,
> = T extends `${infer _F}${infer O}`
  ? U extends T
    ? true
    : EndsWith<O, U>
  : false

02757-medium-partialbykeys [它接收两个类型参数TKK指定应设置为可选的T的属性集。当没有提供K时,它就和普通的 Partial<T> 一样使所有属性都是可选的]

type PartialByKeysMerge<T> = {
  [P in keyof T]: T[P]
}

type PartialByKeys<T, K = keyof T> = PartialByKeysMerge<
  {
    [P in keyof T & K]?: T[P]
  } & {
    [P in Exclude<keyof T, K>]: T[P]
  }
>

02759-medium-requiredbykeys [实现一个通用的 RequiredByKeys<T, K>,它接收两个类型参数TKK指定应设为必选的T的属性集。当没有提供K时,它就和普通的 Required<T> 一样使所有的属性成为必选的]

type PartialByKeysMerge<T> = {
  [P in keyof T]: T[P]
}

type PartialByKeys<T, K = keyof T> = PartialByKeysMerge<
  {
    [P in keyof T & K]?: T[P]
  } & {
    [P in Exclude<keyof T, K>]: T[P]
  }
>

02793-medium-mutable [取出 readonly 修饰词]

type Mutable<T extends object> = {
  -readonly [P in keyof T]: T[P]
}

02852-medium-omitbytype [移除对象中值符合要求类型的键 ]

type OmitByType<T, U> = {
  [P in keyof T as T[P] extends U ? never : P]: T[P]
}

02946-medium-objectentries [Object.entries 的类型定义]

type ObjectEntries<T, K = keyof T, O = Required<T>> = K extends keyof O
  ? [K, O[K] extends [never] ? undefined : O[K]]
  : undefined

03062-medium-shift [Array.shift 的类型定义]

type Shift<T> = T extends [infer _F, ...infer O] ? O : never

03188-medium-tuple-to-nested-object [Given a tuple type T that only contains string type, and a type U, build an object recursively.]

type TupleToNestedObject<T, U> = T extends [infer F, ...infer O]
  ? {
      [P in F & string]: TupleToNestedObject<O, U>
    }
  : U

F & stringF 类型的进一步限定还是很常用的

03192-medium-reverse [Array.reverse 类型定义]

type Reverse<T, R extends unknown[] = []> = T extends [infer F, ...infer O]
  ? Reverse<O, [F, ...R]>
  : R

03196-medium-flip-arguments [翻转函数参数]

type FlipArguments<T> = T extends (...args: infer A) => infer R
  ? (...args: Reverse<A>) => R
  : never

03243-medium-flattendepth [数组扁平化(可以指定深度)]

type FlattenDepth<
  T,
  N extends number = 1,
  U extends any[] = [],
  C extends any[] = [],
> = T extends [infer F, ...infer O]
  ? F extends any[]
    ? N extends C['length']
      ? FlattenDepth<O, N, [...U, F], C>
      : FlattenDepth<O, N, [...U, ...FlattenDepth<F, N, [], [...C, 1]>], C>
    : FlattenDepth<O, N, [...U, F], C>
  : U

03326-medium-bem-style-string [BEM 类型定义]

/**
 * 如果是 "" 将会使用 D 进行替换
 */
type BEM_REPLACE_EMPTY<S extends string, D = never> = S extends '' ? D : S
type BEM_FLAT_M<
  BE extends string,
  M extends any[],
  R extends string = '',
> = M extends [infer F, ...infer O]
  ? BEM_FLAT_M<BE, O, BEM_REPLACE_EMPTY<`${BE}--${F & string}` | R>>
  : BEM_REPLACE_EMPTY<R, BE>

type BEM_FLAT_E<
  B extends string,
  E extends any[],
  R extends string = '',
> = E extends [infer F, ...infer O]
  ? BEM_FLAT_E<B, O, BEM_REPLACE_EMPTY<`${B}__${F & string}` | R>>
  : BEM_REPLACE_EMPTY<R, B>

type BEM<B extends string, E extends string[], M extends string[]> = BEM_FLAT_M<
  BEM_FLAT_E<B, E>,
  M
>

建议修改一下测试文件

import type { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<BEM<'btn', ['price'], []>, 'btn__price'>>,
  Expect<Equal<BEM<'btn', ['price'], ['warning', 'success']>, 'btn__price--warning' | 'btn__price--success' >>,
  Expect<Equal<BEM<'btn', [], ['small', 'medium', 'large']>, 'btn--small' | 'btn--medium' | 'btn--large' >>,
  Expect<Equal<BEM<'btn', [], []>, 'btn'>>,
  Expect<Equal<BEM<'btn', ['price', 'size'], ['small', 'medium', 'large']>, 'btn__price--small' | 'btn__size--small' | 'btn__price--medium' | 'btn__size--medium' | 'btn__price--large' | 'btn__size--large'>>,
]

03376-medium-inordertraversal [Implement the type version of binary tree inorder traversal]

interface TreeNode {
  val: number
  left: TreeNode | null
  right: TreeNode | null
}
type InorderTraversal<
  T extends TreeNode | null,
  TL = T extends TreeNode ? T['left'] : null,
  TR = T extends TreeNode ? T['right'] : null,
  R extends number[] = [],
> = T extends TreeNode
  ? [
      ...(TL extends TreeNode ? InorderTraversal<TL> : []),
      T['val'],
      ...(TR extends TreeNode ? InorderTraversal<TR> : []),
    ]
  : R

*04179-medium-flip [交换对象的 key 和 value]

type Flip<T> = {
  [P in keyof T as `${T[P] & (string | number | bigint | boolean | null | undefined)}`]: P
}

此处可以关注一些 as 的使用方式

04182-medium-fibonacci-sequence [未处理]

04260-medium-nomiwase [列出字符串的所有排列组合]

type Combinations<T extends string, Y extends string> =
  | T
  | Y
  | `${T}${Y}`
  | `${Y}${T}`

type CombinationUnion<S extends string, Y extends string = S> = S extends Y
  ? Combinations<S, CombinationUnion<Exclude<Y, S>>>
  : never

type AllCombinationsStringToUnion<
  S,
  R extends string = '',
> = S extends `${infer F}${infer O}`
  ? AllCombinationsStringToUnion<O, R | F>
  : R

type AllCombinations<S extends string> =
  | ''
  | CombinationUnion<AllCombinationsStringToUnion<S>>

04425-medium-greater-than [比较数字大小]

type GreaterThan<
  T extends number,
  U extends number,
  C extends any[] = [],
> = T extends U
  ? false
  : T extends C['length']
    ? false
    : U extends C['length']
      ? true
      : GreaterThan<T, U, [...C, 1]>

04471-medium-zip [type exp = Zip<[1, 2], [true, false]> ⇒ [[1, true], [2, false]]]

type Zip<A extends any[], A2 extends any[], R extends any[] = []> = A extends [
  infer F,
  ...infer O,
]
  ? A2 extends [infer F2, ...infer O2]
    ? Zip<O, O2, [...R, [F, F2]]>
    : R
  : R

04484-medium-istuple [判断 tuple 类型]

type IsTuple<T> = [T] extends [never]
  ? false
  : T extends readonly [infer _F]
    ? true
    : T extends []
      ? true
      : false

04499-medium-chunk [type exp1 = Chunk<[1, 2, 3], 2> ⇒ [[1, 2], [3]]]

type Chunk<
  T extends any[],
  N extends number,
  R extends any[] = [],
  R2 extends any[] = [],
  C extends any[] = [],
> = T extends [infer F, ...infer O]
  ? C['length'] extends N
    ? Chunk<T, N, [...R, R2], [], []>
    : Chunk<O, N, R, [...R2, F], [...C, 1]>
  : [] extends R2
      ? [...R]
      : [...R, R2]

*04518-medium-fill [fill 函数的类型定义]

type Fill<
  T extends unknown[],
  N,
  Start extends number = 0,
  End extends number = T['length'],
  S extends any[] = [],
  R extends any[] = [],
> = T extends [infer F, ...infer O]
  ? S['length'] extends Start
    ? R['length'] extends End
      ? [...R, F, ...O]
      : Fill<O, N, Start, End, S, [...R, N]>
    : Fill<O, N, Start, End, [...S, F], [...R, F]>
  : R

04803-medium-trim-right [trim-right 的类型定义]

type TrimCharacter3 = ' ' | '\n' | '\t'

type TrimRight<
  S extends string,
  B extends string = '',
  R extends string = '',
> = S extends `${infer F}${infer O}`
  ? F extends TrimCharacter3
    ? TrimRight<O, `${B}${F}`, `${R}`>
    : TrimRight<O, `${B}${F}`, `${B}${F}`>
  : R

05117-medium-without [Without<[1, 2, 4, 1, 5], [1, 2]>; ⇒ [4, 5]]

type Without<T, U, R extends any[] = []> = T extends [infer F, ...infer O]
  ? U extends [infer FU, ...infer FO]
    ? Without<Without<T, FU>, FO>
    : F extends U
      ? Without<O, U, R>
      : Without<O, U, [...R, F]>
  : R

05140-medium-trunc [移除小数点后面的数字]

type Trunc<
  S extends string | number,
  R extends string = '',
> = `${S}` extends `${infer F}${infer O}`
  ? F extends '.'
    ? R
    : Trunc<O, `${R}${F}`>
  : R

这里讲数字转换为字符串的方式值得注意

05153-medium-indexof [indexof 方法的类型定义]

type IndexOf<T, U, C extends any[] = []> = T extends [infer F, ...infer O]
  ? Equal<F, U> extends true
    ? C['length']
    : IndexOf<O, U, [...C, 1]>
  : -1

05310-medium-join [join 方法的类型定义]

type Join<
  T extends any[],
  U extends string | number,
  R extends string = '',
> = T extends [infer F, ...infer O]
  ? R extends ''
    ? Join<O, U, `${R}${F & string}`>
    : Join<O, U, `${R}${U}${F & string}`>
  : R

05317-medium-lastindexof [lastIndexOf 方法的类型定义]

type LastIndexOf<
  T,
  U,
  L extends any[] = [],
  R extends any[] | -1 = -1,
> = T extends [infer F, ...infer O]
  ? Equal<F, U> extends true
    ? R extends any[]
      ? LastIndexOf<O, U, [], [...L, ...R, 1]>
      : LastIndexOf<O, U, [], [...L]>
    : LastIndexOf<O, U, [...L, 1], R>
  : R extends any[]
    ? R['length']
    : R

05360-medium-unique [数组中重复的项目只保留一个]

type UniqueInclude<T, U extends any[]> = U extends [infer F, ...infer O]
  ? Equal<F, T> extends true
    ? true
    : UniqueInclude<T, O>
  : false

type Unique<T, R extends any[] = []> = T extends [infer F, ...infer O]
  ? UniqueInclude<F, R> extends true
    ? Unique<O, R>
    : Unique<O, [...R, F]>
  : R

05821-medium-maptypes [替换对象中值的类型]

type MapTypes<
  T extends Object,
  R extends {
    mapFrom: unknown
    mapTo: unknown
  },
> = {
  [P in keyof T]: T[P] extends R['mapFrom']
    ? R extends { mapFrom: T[P] }
      ? R['mapTo']
      : never
    : T[P]
}

07544-medium-construct-tuple [创建指定长度的数组]

type ConstructTuple<
  L extends number,
  A extends any[] = [],
> = A['length'] extends L ? A : ConstructTuple<L, [unknown, ...A]>

利用数组进行计数只是一个无奈之举

08640-medium-number-range

type NumberRange<L, H, A extends any[] = [], R = ''> = A['length'] extends L
  ? NumberRange<L, H, [...A, 1], A['length']>
  : A['length'] extends H
  ? R | A['length']
  : NumberRange<L, H, [...A, 1], R | A['length']>

*08767-medium-combination [数组转换成union,然后列出union所有排列组合]

type UnionCombination<T extends string, Y extends string = T> = T extends Y
  ? T | `${T} ${UnionCombination<Exclude<Y, T>>}`
  : never

type ArrToUnion<T, R extends string | never = never> = T extends [
  infer F,
  ...infer O,
]
  ? ArrToUnion<O, R | (F & string)>
  : R

type Combination<T extends any[]> = UnionCombination<ArrToUnion<T>>

08987-medium-subsequence [Subsequence<[1, 2]> ⇒ [] | [1] | [2] | [1, 2]]

type Subsequence<T extends any[], R = []> = T extends [infer F, ...infer O]
  ? Subsequence<O, [F, ...Subsequence<O>] | R>
  : R