type-challenges
类型挑战
实现一个类型Zip<T,U>,T和U必须是Tuple
type exp=Zip<[1,2],[true,false]>//应为[[1,true],[2,false]]
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
实现
Array.lastIndexOf
,lastIndexOf<T,U>
的类型版本。获取数组T,任意U,并返回数组T中最后一个U的索引
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
实现类似Vue的简化版本的键入支持。
通过提供函数名SimpleVue
(类似于Vue.extend
或defineComponent
),它应该在computed
和方法中正确地推断出该类型。
在这个挑战中,我们假设SimpleVue
将带有数据、计算和方法字段的Object
作为唯一参数,
data
是一个简单的函数,它返回一个公开上下文的对象,但您无法访问其他计算值或方法。
computed
是一个函数的对象,它将上下文作为对象,进行一些计算并返回结果。计算结果应作为纯返回值而不是函数公开给上下文。
方法是一个函数的对象,也将上下文作为对象。方法可以访问由数据、计算以及其他方法公开的字段。计算的不同之处在于,方法按原样公开为函数。
SimpleVue
返回值的类型可以是任意的。
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
Currying是一种将一个包含多个参数的函数转换为一个包含单个参数的函数序列的技术
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
实现高级util-type
UnionToIntersection<U>
type UnionToIntersection<U> = (U extends any ? (K: U) => any : never) extends (args: infer V) => void ? V : never
实现高级util类型
GetRequired<T>
,它保留所有必需字段
type GetRequired<T> = {
[P in keyof T as T[P] extends Required<T>[P] ? P : never]: T[P]
}
实现高级util类型
GetOptional<T>
,它保留所有可选字段
type GetOptional<T> = Omit<T, keyof {
[P in keyof T as T[P] extends Required<T>[P] ? P : never]: T[P]
}>
分析输入字符串并提取格式占位符,如%d和%f
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
编写一个实用程序类型
IsAny<T>
,它接受输入类型T
。如果T
为any
,则返回true
,否则返回false
。
type IsAny<T> = (<S>() => S extends any ? 1 : 2) extends (<S>() => S extends T ? 1 : 2)
? true
: false
lodash中的get函数是访问JavaScript中嵌套值的非常方便的助手。然而,当我们使用TypeScript时,使用这样的函数会使您丢失类型信息
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
将字符串文本转换为数字,其行为类似于
Number.parseInt
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
实现类型
FilterOut<T,F>
,该类型从元组T中过滤出给定类型F
的项
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
实现一个计算模板字符串长度的类型
LengthOfString<S>
该类型必须支持几百个字符长的字符串(字符串长度的通常递归计算受到TS中递归函数调用深度的限制,即,它支持最长约45个字符的字符串)。
type LengthOfString<S extends string, U extends any[] = []> = S extends `${infer R}${infer V}`
? LengthOfString<V, [...U, any]>
: U['length']
创建一个类型安全的字符串连接函数
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>
实现一个扩展实用程序类型
Pick
的类型DeepPick
,一个类型有两个参数
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>>
从字符串中删除指定的字符
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
众所周知的
split()
方法通过查找分隔符将字符串拆分为子字符串数组,并返回新数组。这个挑战的目标是通过使用分隔符(但在类型系统中)分割字符串!
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[]
实现类型
IsPalindrome<T>
以检查字符串或数字是否为回文。
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
实现
Lodash.intersection
的类型版本,略有不同。交集获取包含多个数组或任何类型元素(包括并集类型)的数组T,并返回包含所有交集元素的新并集
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
实现
BinaryToDecimal<S>
,它采用由0和1组成的精确字符串类型S
,并在S
被视为二进制时返回与S
对应的精确数字类型。您可以假设S
的长度等于或小于8,并且S
不为空。
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>>
给定一个整数
nums
数组和一个整数目标,如果数组中存在两个数字相加等于目标,则返回true。
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
有一个目标对象和一个对象源数组。需要将属性从源复制到目标,如果它与源具有相同的属性,则应始终保留源属性,并删除目标属性。
// 第一次解
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
实现类型Maximum,获取类似于类型T的数组,并返回T中的最大值,
0<=T[number]<1000
,因此不考虑负数。
如果T是空数组[]
,只需reutrn never
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
将对象的键大写,如果值是数组,则遍历数组中的对象
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]>
}
给定要替换的类型的并集和类型对数组,返回用类型对替换的新并集
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
打印整数1到N,以下情况除外:
如果整数可被3整除,则打印“Fizz”
如果整数可被5整除,则打印“Buzz”
如果整数可被3和5整除,则打印“FizzBuzz”。
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]>
给定一个字母串序列,例如AAABCCXXXXXXY。返回游程长度编码字符串3AB2C6XY。还要为该字符串制作解码器
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]>
}
创建一个
SnakeCase<T>
泛型,将camelCase
格式的字符串转换为snake_case
格式的字符串。
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
在类型系统中实现JavaScript
Array.slice
函数。Slice
采用三个参数。输出应该是从索引Start
到End
的Arr
子数组。负数的索引应从倒数开始计数。
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>>