TS类型挑战

type-challenges


类型挑战



4471・Zip

实现一个类型Zip<T,U>,T和U必须是Tuple

type exp=Zip<[1,2],[true,false]>//应为[[1,true],[2,false]]

➡️To TypeScript PlayGround

type Zip<T, U, C extends any[] = []> = T extends [infer R, ...infer S] 
  ? U extends [infer T, ...infer V]
    ? Zip<[...S], [...V], [...C, [R, T]]>
    : C
  : C

5317・LastIndexOf

实现Array.lastIndexOflastIndexOf<T,U>的类型版本。获取数组T,任意U,并返回数组T中最后一个U的索引

➡️To TypeScript PlayGround

type isEqual<T, U> = (<S>() =>  S extends T ? 1 : 2) extends (<S>() =>  S extends U ? 1 : 2) 
  ? true 
  : false

type LastIndexOf<T, U> = T extends [...infer R, infer S] 
  ? isEqual<S, U> extends true
    ? [...R]['length'] 
    : LastIndexOf<[...R], U>
  : -1

6・Simple Vue

实现类似Vue的简化版本的键入支持。
通过提供函数名SimpleVue(类似于Vue.extenddefineComponent),它应该在computed和方法中正确地推断出该类型。
在这个挑战中,我们假设SimpleVue将带有数据、计算和方法字段的Object作为唯一参数,
data是一个简单的函数,它返回一个公开上下文的对象,但您无法访问其他计算值或方法。
computed是一个函数的对象,它将上下文作为对象,进行一些计算并返回结果。计算结果应作为纯返回值而不是函数公开给上下文。
方法是一个函数的对象,也将上下文作为对象。方法可以访问由数据、计算以及其他方法公开的字段。计算的不同之处在于,方法按原样公开为函数。
SimpleVue返回值的类型可以是任意的。

➡️To TypeScript PlayGround

type MyReturnType<T> = T extends () => infer R ? R : T

type Option<D, C, M> = {
  data: (this:{}) => D,
  computed: C & ThisType<D>,
  methods: M & ThisType<D & {
    [P in keyof C]: MyReturnType<C[P]>
  } & M>
}

declare function SimpleVue<D, C, M>(options: Option<D, C, M>): any

17・Currying 1

Currying是一种将一个包含多个参数的函数转换为一个包含单个参数的函数序列的技术

➡️To TypeScript PlayGround

declare function Currying<T>(fn: T): T extends () => any ? T : Loop<T>

type Loop<T> = T extends (...args: infer R) => any 
  ? R extends [infer V, ...infer S]
    ? (arg: V) => Loop<(...args: S) => ReturnType<T>>
    : ReturnType<T>
  : T

55・Union to Intersection

实现高级util-type UnionToIntersection<U>

➡️To TypeScript PlayGround

type UnionToIntersection<U> = (U extends any ? (K: U) => any : never) extends (args: infer V) => void ? V : never

57・Get Required

实现高级util类型GetRequired<T>,它保留所有必需字段

➡️To TypeScript PlayGround

type GetRequired<T> = {
  [P in keyof T as T[P] extends Required<T>[P] ? P : never]: T[P]
}

59・Get Optional

实现高级util类型GetOptional<T>,它保留所有可选字段

➡️To TypeScript PlayGround

type GetOptional<T> = Omit<T, keyof {
  [P in keyof T as T[P] extends Required<T>[P] ? P : never]: T[P]
}>

147・C-printf Parser

分析输入字符串并提取格式占位符,如%d和%f

➡️To TypeScript PlayGround

type ControlsMap = {
  c: 'char'
  s: 'string'
  d: 'dec'
  o: 'oct'
  h: 'hex'
  f: 'float'
  p: 'pointer'
}

type ParsePrintFormat<T, U extends string[] = []> = T extends `${infer R}%${infer V}${infer S}`
  ? ParsePrintFormat<`${S}`, V extends keyof ControlsMap ? [...U, ControlsMap[V]] : U>
  : U

223・IsAny

编写一个实用程序类型IsAny<T>,它接受输入类型T。如果Tany,则返回true,否则返回false

➡️To TypeScript PlayGround

type IsAny<T> = (<S>() => S extends any ? 1 : 2) extends (<S>() => S extends T ? 1 : 2) 
  ? true 
  : false

270・Typed Get

lodash中的get函数是访问JavaScript中嵌套值的非常方便的助手。然而,当我们使用TypeScript时,使用这样的函数会使您丢失类型信息

➡️To TypeScript PlayGround

type Get<T, K> = K extends keyof T
  ? T[K]
  : K extends `${infer R}.${infer S}`
    ? R extends keyof T
      ? Get<T[R], S>
      : never
    : never

300・String to Number

将字符串文本转换为数字,其行为类似于Number.parseInt

➡️To TypeScript PlayGround

type ToTuple<T, U extends string[] = []> = T extends `${infer R}${infer S}` ? ToTuple<S, [...U, R]> : U

type IsNumber<T> = T extends '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' ? true : false

type IsString<T, F = true> = T extends [infer R, ...infer S] 
  ? IsNumber<R> extends true
    ? IsString<[...S], F>
    : IsString<[...S], false>
  : F

type ToNumber<S extends string, U extends any[] = []> = IsString<ToTuple<S>> extends true
  ? S extends `${U['length']}` 
    ? U['length'] 
    : ToNumber<S, [...U, any]>
  : never

399・Tuple Filter

实现类型FilterOut<T,F>,该类型从元组T中过滤出给定类型F的项

➡️To TypeScript PlayGround

type FilterOut<T extends any[], F, U extends any[] = []> = T extends [infer R, ...infer S]
  ? FilterOut<[...S], F, [R] extends [F] ? U : [...U, R]>
  : U

651・Length of String 2

实现一个计算模板字符串长度的类型LengthOfString<S>
该类型必须支持几百个字符长的字符串(字符串长度的通常递归计算受到TS中递归函数调用深度的限制,即,它支持最长约45个字符的字符串)。

➡️To TypeScript PlayGround

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

847・String Join

创建一个类型安全的字符串连接函数

➡️To TypeScript PlayGround

type Loop<T, U extends string, V extends string = ''> = T extends [infer R extends string, ...infer S] 
  ? V extends ''
    ? Loop<[...S], U, `${R}`>
    : Loop<[...S], U, `${V}${U}${R}`>
  : V
  
declare function join<D extends string>(delimiter: D): <P extends string[]>(...parts: P) => Loop<P, D> 

956・DeepPick

实现一个扩展实用程序类型Pick的类型DeepPick,一个类型有两个参数

➡️To TypeScript PlayGround

type UnToIn<T> = (T extends any ? (args: T) => any : never) extends (args: infer R) => any ? R : never

type RePick<T, U> = U extends [...infer S, infer R extends string]
  ? RePick<{[P in keyof {R: R} as `${R}`]: T}, [...S]>
  : T

type MyPick<T, U extends string, V extends string[] = []> = U extends ''
  ? never
  : U extends `${infer R extends string & keyof T}.${infer S}`
    ? MyPick<T[R], S, [...V, R]>
    : U extends keyof T
      ? RePick<{[P in keyof {U: U} as `${U}`]: T[U]}, V>
      : unknown

type DeepPick<T, U  extends string> = UnToIn<MyPick<T, U>>

2059・Drop String

从字符串中删除指定的字符

➡️To TypeScript PlayGround

type ToTuple<T, U extends any[] = []> = T extends `${infer R}${infer S}`
  ? ToTuple<S, [...U, R]>
  : U

type ToString<T, U extends string = ''> = T extends [infer R extends string, ...infer S]
  ? ToString<[...S], `${U}${R}`>
  : U

type DeDup<T, U, E extends any[] = []> = T extends [infer R, ...infer S]
  ? DeDup<[...S], U, R extends U ? E : [...E, R]>
  : ToString<E>

type DropString<T, U> = ToTuple<U> extends [infer R, ...infer S]
  ? DropString<DeDup<ToTuple<T>, R>, ToString<[...S]>>
  : T

2822・Split

众所周知的split()方法通过查找分隔符将字符串拆分为子字符串数组,并返回新数组。这个挑战的目标是通过使用分隔符(但在类型系统中)分割字符串!

➡️To TypeScript PlayGround

type isEqual<T, U> = (<S>() => S extends T ? 1 : 2) extends (<S>() => S extends U ? 1 : 2) 
  ? true 
  : false

type Split<S extends string, SEP extends string> = isEqual<S, string> extends false
  ? SEP extends ''
    ? S extends `${infer R}${infer V}`
      ? [R, ...Split<V, SEP>]
      : []
    : S extends `${infer R}${SEP}${infer V}`
      ? [R, ...Split<V, SEP>]
      : [S]
  : S[]

4037・IsPalindrome

实现类型 IsPalindrome<T> 以检查字符串或数字是否为回文。

➡️To TypeScript PlayGround

type ToTuple<T extends string|number, U extends string[] = []> = `${T}` extends `${infer R}${infer S}`
  ? ToTuple<S, [...U, R]>
  : U

type ToString<T, U extends string = ''> = T extends [infer R extends string, ...infer S]
  ? ToString<[...S], `${U}${R}`>
  : U
  
type IsPalindrome<T extends string|number> = ToTuple<T> extends [infer R, ...infer S, infer V]
  ? R extends V
    ? IsPalindrome<ToString<[...S]>>
    : false
  : true

5423・Intersection

实现Lodash.intersection的类型版本,略有不同。交集获取包含多个数组或任何类型元素(包括并集类型)的数组T,并返回包含所有交集元素的新并集

➡️To TypeScript PlayGround

type ToUnion<T> = T extends [infer R, ...infer S] 
  ? R | ToUnion<[...S]> 
  : T extends []
    ? never
    : T

type Every<T, U, F extends boolean = true> = U extends [infer R, ...infer S]
  ? Every<T, [...S], [T extends ToUnion<R> ? T : never] extends [never] ? false : F>
  : F

type Loop<T, U, E extends any = never> = T extends [infer R, ...infer S]
  ? Loop<[...S], U, Every<R, U> extends true ? E | R : E>
  : E

type Intersection<T> = T extends [infer R, ...infer S]
  ? Loop<R, [...S]>
  : never

6141・Binary to Decimal

实现BinaryToDecimal<S>,它采用由0和1组成的精确字符串类型S,并在S被视为二进制时返回与S对应的精确数字类型。您可以假设S的长度等于或小于8,并且S不为空。

➡️To TypeScript PlayGround

type StringToTuple<T extends string, U extends number[] = []> = T extends `${infer R}${infer S}`
  ? StringToTuple<S, [...U, R extends '0' ? 0 : 1]>
  : U

type NumToTuple<T, U extends any[] = []> = T extends U['length'] 
  ? U 
  : NumToTuple<T, [...U, any]>

type Add<T, U> =  [...NumToTuple<T>, ...NumToTuple<U>]['length']

type Trans<T, N = 1, U = 0> = T extends [...infer S, infer R]
  ? Trans<S, Add<N, N>, Add<U, R extends 1 ? N : 0>>
  : U

type BinaryToDecimal<S extends string> = Trans<StringToTuple<S>>

8804・Two Sum

给定一个整数nums数组和一个整数目标,如果数组中存在两个数字相加等于目标,则返回true。

➡️To TypeScript PlayGround

type ToNum<T, U extends unknown[] = []> = T extends U['length'] 
    ? U 
    : ToNum<T, [...U, unknown]>

type Add<T, U> = ToNum<T> extends [infer R, ...infer S] 
    ? Add<[...S]['length'], [...ToNum<U>, R]['length']> 
    : U

type OneSum<T, U, F extends boolean = false> = T extends [infer R, infer V, ...infer S] 
    ? (Add<R, V> extends U 
        ? OneSum<[R, ...S], U, true> 
        : OneSum<[R, ...S], U, F>) 
    : F

type TwoSum<T, U, F extends boolean = false> = T extends [infer R, ...infer S] 
    ? (OneSum<T, U> extends true 
        ? TwoSum<[...S], U, true> 
        : TwoSum<[...S], U, F>) 
    : F

9160・Assign

有一个目标对象和一个对象源数组。需要将属性从源复制到目标,如果它与源具有相同的属性,则应始终保留源属性,并删除目标属性。

➡️To TypeScript PlayGround

// 第一次解
type SameKey<T, U> = keyof Pick<T, Extract<keyof T, keyof U>>

type DiffKey<T, U> = Exclude<keyof T | keyof U, SameKey<T, U>>

type CaseAsign<T, U> = {
  [P in (keyof T | keyof U)]: P extends SameKey<T, U> 
                                ? U[P] 
                                : P extends DiffKey<T, U> 
                                  ? P extends keyof T
                                    ? T[P]
                                    : P extends keyof U
                                      ? U[P]
                                      : never
                                  : never
}

type Assign<T extends Record<string, unknown>, U> = U extends [infer R, ...infer S]
  ? R extends string|number
    ? T
    : Assign<CaseAsign<T, R>, [...S]>
  : T


// 第二次解
type AssignObj<T, U> = U extends object 
  ? {
      [P in keyof T as P extends keyof U ? never : P]: T[P]
    } & U
  : T

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

type Assign<T extends Record<string, unknown>, U> = U extends [infer R, ...infer S]
  ? Assign<Refresh<AssignObj<T, R>>, [...S]>
  : T

9384・Maximum

实现类型Maximum,获取类似于类型T的数组,并返回T中的最大值,0<=T[number]<1000,因此不考虑负数。
如果T是空数组[],只需reutrn never

➡️To TypeScript PlayGround

type ToTuple<T, U extends unknown[] = []> = T extends U['length'] 
  ? U 
  : ToTuple<T, [...U, unknown]>

type Compare<T, U> = T extends [infer R, ...infer S]
  ? U extends [infer V, ...infer Q]
    ? Compare<[...S], [...Q]>
    : true
  : false

type Maximum<T extends any[], U = never> = T extends [infer R, ...infer S] 
  ? Compare<ToTuple<R>, ToTuple<U>> extends true
    ? Maximum<[...S], R>
    : Maximum<[...S], U>
  : U 

9775・Capitalize Nest Object Keys

将对象的键大写,如果值是数组,则遍历数组中的对象

➡️To TypeScript PlayGround

type UpperCase = {
  "a": "A"
  "b": "B"
  "c": "C"
  "d": "D"
  "e": "E"
  "f": "F"
  "g": "G"
  "h": "H"
  "i": "I"
  "j": "J"
  "k": "K"
  "l": "L"
  "m": "M"
  "n": "N"
  "o": "O"
  "p": "P"
  "q": "Q"
  "r": "R"
  "s": "S"
  "t": "T"
  "u": "U"
  "v": "V"
  "w": "W"
  "x": "X"
  "y": "Y"
  "z": "Z"
}

type Capitalize<T> = T extends keyof UpperCase ? UpperCase[T] : T

type DeepCap<T, U extends any[]= []> = T extends [infer A, ...infer R] 
  ? DeepCap<[...R], [...U, CapitalizeNestObjectKeys<A>]>
  : T extends []
    ? U
    : T

type CapitalizeNestObjectKeys<T> = {
  [P in keyof T as P extends `${infer R}${infer S}` 
	  ? `${Capitalize<R>}${S}` 
	  : P
  ]: DeepCap<T[P]>
}

13580・Replace Union

给定要替换的类型的并集和类型对数组,返回用类型对替换的新并集

➡️To TypeScript PlayGround

type Replace<T, U> = U extends [infer R, infer S]
  ? T extends R
    ? S
    : T
  : never

type UnionReplace<T, U extends [any, any][]> = U extends [infer R, ...infer S extends any[]]
  ? UnionReplace<Replace<T, R>, [...S]>
  : T

14080・FizzBuzz

打印整数1到N,以下情况除外:
如果整数可被3整除,则打印“Fizz”
如果整数可被5整除,则打印“Buzz”
如果整数可被3和5整除,则打印“FizzBuzz”。

➡️To TypeScript PlayGround

type ToTuple<T, U extends any[] = []> = T extends U['length'] 
  ? U 
  : ToTuple<T, [any, ...U]>

type Dec<U extends number> = ToTuple<U> extends [infer R, ...infer S] 
  ? [...S]['length'] 
  : -1

type Fizz<T> = ToTuple<T> extends [infer A, infer B, infer C, ...infer S] 
  ? Fizz<[...S]['length']> 
  : T

type Buzz<T> = ToTuple<T> extends [infer A, infer B, infer C, infer D, infer E, ...infer S] 
  ? Buzz<[...S]['length']> 
  : T

type Format<T extends number> = Fizz<T> extends 0
  ? Buzz<T> extends 0
    ? 'FizzBuzz'
    : 'Fizz'
  : Buzz<T> extends 0
    ? 'Buzz'
    : `${T}`

type FizzBuzz<N extends number, R extends string[] = []> =  Dec<N> extends -1 
  ? R 
  : FizzBuzz<Dec<N>, [Format<N>, ...R]>

14188・Run-length encoding

给定一个字母串序列,例如AAABCCXXXXXXY。返回游程长度编码字符串3AB2C6XY。还要为该字符串制作解码器

➡️To TypeScript PlayGround

namespace RLE {
  type EnCodeString<T extends string[]> = T extends [infer Q extends string, ...infer W] 
    ? `${T['length'] extends 1 ? '' : T['length']}${Q}`
    : ''

  export type Encode<T extends string, U extends string[] = [''], C extends string = ''> = T extends `${infer R}${infer S}` 
    ? U extends [infer Q extends string, ...infer W]
      ? Q extends R
        ? Encode<S, [...U, R], C>
        : Encode<S, [R], `${C}${EnCodeString<U>}`>
      : never
    : `${C}${EnCodeString<U>}`


  type DeCodeString<T extends string, C extends string[] = []> =  T extends `${infer D}${infer Y}${infer V}`
    ? D extends `${C['length']}`
      ? ToString<C>
      : DeCodeString<T, [...C, Y]>
    : T
    
  type ToString<T extends string[], U extends string = ''> = T extends [infer R extends string, ...infer S extends string[]] 
    ? ToString<[...S], `${U}${R}`>
    : U
  
  type ConcatStr<T, U extends string = ''> = T extends [infer R extends string, ...infer S] 
    ? ConcatStr<[...S], `${U}${DeCodeString<R>}`>
    : U

  export type Decode<T extends string, C extends string[] = []> =  T extends `${infer D}${infer Y}${infer V}`
    ? D extends `${number}`
      ? Decode<V, [...C, `${D}${Y}`]>
      : Decode<`${Y}${V}`, [...C, D]>
    : ConcatStr<[...C, T]>
}

19458・SnakeCase

创建一个SnakeCase<T>泛型,将camelCase格式的字符串转换为snake_case格式的字符串。

➡️To TypeScript PlayGround

type LowerCase = {
  "A": "a"
  "B": "b"
  "C": "c"
  "D": "d"
  "E": "e"
  "F": "f"
  "G": "g"
  "H": "h"
  "I": "i"
  "J": "j"
  "K": "k"
  "L": "l"
  "M": "m"
  "N": "n"
  "O": "o"
  "P": "p"
  "Q": "q"
  "R": "r"
  "S": "s"
  "T": "t"
  "U": "u"
  "V": "v"
  "W": "w"
  "X": "x"
  "Y": "y"
  "Z": "z"
}

type SnakeCase<T, U extends string = ''> = T extends `${infer R}${infer S}`
  ? SnakeCase<S, R extends keyof LowerCase 
      ? `${U}_${LowerCase[`${R}`]}` 
      : `${U}${R}`
    >
  : U

216・Slice

在类型系统中实现JavaScript Array.slice函数。Slice采用三个参数。输出应该是从索引StartEndArr子数组。负数的索引应从倒数开始计数。

➡️To TypeScript PlayGround

type GetLen<T> = T extends {length: infer R} ? R : 0

type ToTuple<T, U extends any[] = []> = T extends number
  ? T extends GetLen<U>
    ? U 
    : ToTuple<T, [...U, any]>
  : T extends string
    ? T extends `${GetLen<U>}`
      ? U
      : ToTuple<T, [...U, any]>
    : never

type ToPos<T extends number|never, C = GetLen<Arr>> = [T] extends [never]
  ? T
  : `${T}` extends `-${infer R}` 
    ? Des<C, GetLen<ToTuple<R>>> 
    : T

type Des<T, V = 1, U = []> = ToTuple<T> extends [infer R, ...infer S]
  ? ToTuple<V> extends [infer A, ...infer B]
    ? Des<GetLen<[...S]>,GetLen<[...B]>, U>
    : T
  : 0

type Compare<T, U> = ToTuple<T> extends [infer R, ...infer S] 
  ? ToTuple<U> extends [infer V, ...infer W]
    ? Compare<GetLen<[...S]>, GetLen<[...W]>>
    : true
  : false

type Pop<T> = T extends [...infer S, infer R] ? [...S] : T

type Shift<T> = T extends [infer R, ...infer S] ? [...S] : T

type SliceP<T extends any[], End extends number> = [End] extends [never]
  ? T
  : Compare<ToPos<End>, GetLen<T>> extends true
    ? T
    : ToPos<End> extends GetLen<T>
      ? T
      : SliceP<Pop<T>, End>

type SliceS<T extends any[], Start> = [Start] extends [never]
  ? T
  : Start extends 0
    ? T
    : SliceS<Shift<T>, Des<Start>>

type Slice<Arr extends any[], Start extends number=never, End extends number=never> = SliceS<SliceP<Arr, End>, ToPos<Start>>
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Raccom

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值