TypeScript 类型挑战 Medium
[[toc]]
- 项目地址 Github
- 项目描述: 高质量的类型可以帮助提高项目的可维护性,同时避免潜在的错误。
Medium 版需要注意的事情
- 这部分挑战有很多使用递归来实现。还涉及很多类型语言的特殊用法。比如如何判断 never。利用数组来计数等等。
Get Return Type
Medium
, #infer
, #built-in
实现 TS 内置的 ReturnType
,但不可以使用它。
const fn = (v: boolean) => {
if (v)
return 1
else
return 2
}
type a = MyReturnType // 应推导出 "1 | 2"
答案
type MyReturnType = T extends (...arg: any) => infer R ? R : never
Omit
Medium
, #union
, #built-in
不使用 Omit 实现 TypeScript 的 Omit
Omit 会创建一个省略 K 中字段的 T 对象。
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyOmit
const todo: TodoPreview = {
completed: false,
}
答案
type MyExclude = T extends K ? never : T
type MyOmit = {
[V in MyExclude]: T[V]
}
Readonly 2
Medium
, #readonly
, #object-keys
实现一个通用MyReadonly2
,它带有两种类型的参数T
和K
。
K
指定应设置为Readonly的T
的属性集。如果未提供K
,则应使所有属性都变为只读,就像普通的Readonly
一样。
interface Todo {
title: string
description: string
completed: boolean
}
const todo: MyReadonly2 = {
title: "Hey",
description: "foobar",
completed: false,
}
todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property
todo.completed = true // OK
答案
type MyReadonly2 = {
[P in keyof T as P extends K ? never : P] : T[P]
} & {
readonly [P in keyof T as P extends K ? P : never] : T[P]
}
Deep Readonly
Medium
, #readonly
, #object-keys
, #deep
实现一个通用的DeepReadonly
,它将对象的每个参数及其子对象递归地设为只读。
您可以假设在此挑战中我们仅处理对象。数组,函数,类等都无需考虑。但是,您仍然可以通过覆盖尽可能多的不同案例来挑战自己。
type X = {
x: {
a: 1
b: 'hi'
}
y: 'hey'
}
type Expected = {
readonly x: {
readonly a: 1
readonly b: 'hi'
}
readonly y: 'hey'
}
type Todo = DeepReadonly // should be same as `Expected`
答案
type DeepReadonly = {
readonly [K in keyof T]: keyof T[K] extends never ? T[K] : DeepReadonly
}
Tuple to Union
Medium
, #infer
, #tuple
, #union
实现泛型TupleToUnion
,它返回元组所有值的合集。
type Arr = ['1', '2', '3']
type Test = TupleToUnion // expected to be '1' | '2' | '3'
答案
type TupleToUnion = T[number]
Chainable Options
Medium
, #application
在 JavaScript 中我们很常会使用可串联(Chainable/Pipeline)的函数构造一个对象,但在 TypeScript 中,你能合理的给他附上类型吗?
在这个挑战中,你可以使用任意你喜欢的方式实现这个类型 - Interface, Type 或 Class 都行。你需要提供两个函数 option(key, value)
和 get()
。在 option
中你需要使用提供的 key 和 value 扩展当前的对象类型,通过 get
获取最终结果。
declare const config: Chainable
const result = config
.option('foo', 123)
.option('name', 'type-challenges')
.option('bar', { value: 'Hello World' })
.get()
// 期望 result 的类型是:
interface Result {
foo: number
name: string
bar: {
value: string
}
}
你只需要在类型层面实现这个功能 - 不需要实现任何 TS/JS 的实际逻辑。
你可以假设 key
只接受字符串而 value
接受任何类型,你只需要暴露它传递的类型而不需要进行任何处理。同样的 key
只会被使用一次。
答案
type Chainable = {
option(key: K, value: V)
: Chainable
get(): T
}
Last of Array
Medium
, #array
实现一个通用Last
,它接受一个数组T
并返回其最后一个元素的类型。
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
type tail1 = Last // expected to be 'c'
type tail2 = Last // expected to be 1
答案
type Last = T extends [... infer rest, infer L] ? L : undefined
Pop, Shift, Push, Unshift
Medium
, #array
实现一个通用Pop
,它接受一个数组T
并返回一个没有最后一个元素的数组。
type arr1 = ['a', 'b', 'c', 'd']
type arr2 = [3, 2, 1]
type re1 = Pop // expected to be ['a', 'b', 'c']
type re2 = Pop // expected to be [3, 2]
额外:同样,您也可以实现Shift
,Push
和Unshift
吗?
答案
type Pop = T extends [... infer R, infer last] ? R : T
type Push = [...T, V]
type Shift = T extends [infer head, ... infer R] ? R : T
type Unshift = [V, ...T]
Promise.all
Medium
, #array
, #built-in
键入函数PromiseAll
,它接受PromiseLike对象数组,返回值应为Promise
,其中T
是解析的结果数组。
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
// expected to be `Promise<[number, 42, string]>`
const p = Promise.all([promise1, promise2, promise3] as const)
答案
declare function PromiseAll(values: readonly [...T])
: Promise<{ [K in keyof T] : T[K] extends Promise ? R : T[K] }>
Type Lookup
Medium
, #union
, `#map
有时,您可能希望根据某个属性在联合类型中查找类型。
在此挑战中,我们想通过在联合类型Cat | Dog
中搜索公共type
字段来获取相应的类型。换句话说,在以下示例中,我们期望LookUp
获得Dog
,LookUp
获得Cat
。
interface Cat {
type: 'cat'
breeds: 'Abyssinian' | 'Shorthair' | 'Curl' | 'Bengal'
}
interface Dog {
type: 'dog'
breeds: 'Hound' | 'Brittany' | 'Bulldog' | 'Boxer'
color: 'brown' | 'white' | 'black'
}
type MyDog = LookUp // expected to be `Dog`
答案
type LookUp = U extends { type: infer T }
? V extends T ? U : never
: never
Trim Left
Medium
, #template-literal
实现 TrimLeft
,它接收确定的字符串类型并返回一个新的字符串,其中新返回的字符串删除了原字符串开头的空白字符串。
type trimed = TrimLeft<' Hello World '> // 应推导出 'Hello World '
答案
type TrimChar = ' ' | '\n' | '\t'
type TrimLeft =
S extends `${TrimChar}${infer R}` ? TrimLeft : S
Trim
Medium
, #template-literal
实现Trim
,它是一个字符串类型,并返回一个新字符串,其中两端的空白符都已被删除。
type trimed = Trim<' Hello World '> // expected to be 'Hello World'
答案
type TrimChar = ' ' | '\n' | '\t'
type TrimLeft =
S extends `${TrimChar}${infer R}` ? TrimLeft : S
type TrimRight =
S extends `${infer L}${TrimChar}` ? TrimRight : S
type Trim = TrimLeft>
Capitalize
Medium
, #template-literal
实现 Capitalize
它将字符串的第一个字母转换为大写,其余字母保持原样。
type capitalized = MyCapitalize<'hello world'> // expected to be 'Hello world'
答案
type MyCapitalize = S extends `${infer H}${infer R}`
? `${Uppercase}${R}` : S
Replace
Medium
, #template-iteral
实现 Replace
将字符串 S
中的第一个子字符串 From
替换为 To
。
type replaced = Replace<'types are fun!', 'fun', 'awesome'>
// 期望是 'types are awesome!'
答案
type Replace =
From extends ''
? S
: S extends `${infer H}${From}${infer E}` ? `${H}${To}${E}` : S
ReplaceAll
Medium
, #template-literal
实现 ReplaceAll
将一个字符串 S
中的所有子字符串 From
替换为 To
。
type replaced = ReplaceAll<'t y p e s', ' ', ''> // 期望是 'types'
答案
type ReplaceAll =
From extends ''
? S
: S extends `${infer H}${From}${infer E}`
? `${H}${To}${ReplaceAll}`
: S
Append Argument
Medium
, #arguments
实现一个泛型 AppendArgument
,对于给定的函数类型 Fn
,以及一个任意类型 A
,返回一个新的函数 G
。G
拥有 Fn
的所有参数并在末尾追加类型为 A
的参数。
type Fn = (a: number, b: string) => number
type Result = AppendArgument
// 期望是 (a: number, b: string, x: boolean) => number
答案
type AppendArgument any, A> =
Fn extends (...args: infer Arg) => infer R
? (...arg: [...Arg, A]) => R
: never
Permutation
Medium
, #union
实现联合类型的全排列,将联合类型转换成所有可能的全排列数组的联合类型。
type perm = Permutation<'A' | 'B' | 'C'>; // ['A', 'B', 'C'] | ['A', 'C', 'B'] | ['B', 'A', 'C'] | ['B', 'C', 'A'] | ['C', 'A', 'B'] | ['C', 'B', 'A']
答案
type Permutation =
[T] extends [never]
? []
: K extends K
? [K, ...Permutation>]
: never
https://github.com/type-challenges/type-challenges/issues/614
Note
T extends never // 不生效
T[] extends never // 不生效
[T] extends [never] // 生效
Length of String
Medium
, #template-literal
计算字符串的长度,类似于 String#length
。
LengthOfString<'kumiko'> // 6
答案
type StringToArray =
S extends `${infer H}${infer R}`
? [H, ...StringToArray]
: []
type LengthOfString = StringToArray['length']
Flatten
Medium
, #array
在这个挑战中,你需要写一个接受数组的类型,并且返回扁平化的数组类型。
type flatten = Flatten<[1, 2, [3, 4], [[[5]]]]> // [1, 2, 3, 4, 5]
答案
type Flatten =
T extends [infer H, ...infer R]
? [
...(H extends any[] ? Flatten : [H]),
...(R extends any[] ? Flatten : [R]),
]
: []
Append to object
Medium
, #object-keys
实现一个为接口添加一个新字段的类型。该类型接收三个参数,返回带有新字段的接口类型。
type Test = { id: '1' }
type Result = AppendToObject // expected to be { id: '1', value: 4 }
答案
type AppendToObject<
T extends {},
U extends string | number | symbol,
V
> = {
[K in keyof T | U]: K extends keyof T
? K extends U
? V
: T[K]
: V
}
Absolute
Medium
, #math
,#template-literal
实现一个接收string,number或bigInt类型参数的Absolute
类型,返回一个正数字符串。
type Test = -100;
type Result = Absolute; // expected to be "100"
答案
type Absolute = `${T}` extends `-${infer X}` ? X : `${T}`
String to Union
Medium
, #union
, #string
实现一个将接收到的String参数转换为一个字母Union的类型。
type Test = '123';
type Result = StringToUnion; // expected to be "1" | "2" | "3"
答案
type StringToUnion =
T extends `${infer H}${infer R}`
? H | StringToUnion
: never
Merge
Medium
, #object
实现联合类型的全排列,将联合类型转换成所有可能的全排列数组的联合类型。
type foo = {
name: string;
age: string;
}
type coo = {
age: number;
sex: string
}
type Result = Merge; // expected to be {name: string, age: number, sex: string}
答案
type Merge = {
[K in (keyof T | keyof S)]: K extends keyof S
? S[K]
: K extends keyof T ? T[K] : never
}
KebabCase
Medium
, #
FooBarBaz
-> foo-bar-baz
type KebabCase<'`FooBarBaz`'> // `foo-bar-baz`
答案
type _KebaCase = S extends `${infer H}${infer R}`
? H extends Lowercase
? `${H}${_KebaCase}`
: `-${Lowercase}${_KebaCase}`
: ''
type KebabCase = S extends `${infer H}${infer R}`
? H extends Lowercase
? `${H}${_KebaCase}`
: `${Lowercase}${_KebaCase}`
: ''
Diff
Medium
, #object
获取两个接口类型中的差值属性。
type Foo = {
a: string;
b: number;
}
type Bar = {
a: string;
c: boolean
}
type Result1 = Diff // { b: number, c: boolean }
type Result2 = Diff // { b: number, c: boolean }
答案
type Diff = {
[K in Exclude | Exclude] :
K extends keyof O
? O[K]
: K extends keyof O1
? O1[K]
: never
}
AnyOf
Medium
, #array
在类型系统中实现类似于 Python 中 any
函数。类型接收一个数组,如果数组中任一个元素为真,则返回 true
,否则返回 false
。如果数组为空,返回 false
。
type Sample1 = AnyOf<[1, '', false, [], {}]> // expected to be true.
type Sample2 = AnyOf<[0, '', false, [], {}]> // expected to be false.
答案
type Falsy = '' | [] | false | Record | 0
type AnyOf = T[number] extends Falsy ? false : true
IsNever
Medium
, #union
, #utils
实现 IsNever
类型, 解析输入 T 类型为 never
返回 true
否则 返回 false
type A = IsNever // expected to be true
type B = IsNever // expected to be false
type C = IsNever // expected to be false
type D = IsNever<[]> // expected to be false
type E = IsNever // expected to be false
答案
type IsNever = [T] extends [never] ? true : false
// note T extends never 无法判定
IsUnion
Medium
, #union
, #utils
实现 IsUnion
类型, 解析输入 T 类型为联合类型 返回 true
否则 返回 false
type case1 = IsUnion // false
type case2 = IsUnion // true
type case3 = IsUnion<[string|number]> // false
答案
type IsUnionImpl =
(T extends T
? C extends T
? true
: unknown
: never
) extends true ? false : true
type IsUnion = IsUnionImpl
ReplaceKeys
Medium
实现 ReplaceKeys
类型, 它将替换联合类型中类型的键值, 如果该类型没有这个Key则跳过,如果有则替换。
type NodeA = {
type: 'A'
name: string
flag: number
}
type NodeB = {
type: 'B'
id: number
flag: number
}
type NodeC = {
type: 'C'
name: string
flag: number
}
type Nodes = NodeA | NodeB | NodeC
type ReplacedNodes = ReplaceKeys // {type: 'A', name: number, flag: string} | {type: 'B', id: number, flag: string} | {type: 'C', name: number, flag: string} // would replace name from string to number, replace flag from number to string.
type ReplacedNotExistKeys = ReplaceKeys // {type: 'A', name: never, flag: number} | NodeB | {type: 'C', name: never, flag: number} // would replace name to never
答案
type ReplaceKeys = {
[K in keyof U]: K extends T
? Y[keyof Y & K]
: U[K]
}
Remove Index Signature
Medium
从对象类型中排除索引签名。
type Foo = {
[key: string]: any;
foo(): void;
}
type A = RemoveIndexSignature // expected { foo(): void }
答案
https://github.com/type-challenges/type-challenges/issues/3542
type RemoveIndexSignature = {
[K in keyof T as K extends `${infer _}` ? K : never]: T[K]
}
// `${infer _}` 不同于 string
// 我们需要将K约束为字符串的值,而不是字符串类型
Percentage Parser
Medium
实现类型 PercentageParser。根据规则 /^(\+|\-)?(\d*)?(\%)?$/
匹配类型 T。
匹配的结果由三部分组成,分别是:[正负号
, 数字
, 单位
],如果没有匹配,则默认是空字符串。
type PString1 = ''
type PString2 = '+85%'
type PString3 = '-85%'
type PString4 = '85%'
type PString5 = '85'
type R1 = PercentageParser // expected ['', '', '']
type R2 = PercentageParser // expected ["+", "85", "%"]
type R3 = PercentageParser // expected ["-", "85", "%"]
type R4 = PercentageParser // expected ["", "85", "%"]
type R5 = PercentageParser // expected ["", "85", ""]
答案
type Prefix = T extends `${infer P}${string}`
? P extends '-' | '+' ? P : ''
: ''
type Suffix = T extends `${string}%` ? '%' : ''
type Num = T extends `${Prefix}${infer R}${Suffix}` ? R : never
type PercentageParser = [Prefix, Num, Suffix]
Drop Char
Medium
从字符串中剔除指定字符。
type Butterfly = DropChar<' b u t t e r f l y ! ', ' '> // 'butterfly!'
答案
type DropChar = S extends `${infer H}${infer R}`
? `${H extends C ? '' : H}${DropChar}`
: S
MinusOne
Medium
, Math
给定一个正整数作为类型的参数,要求返回的类型是该数字减 1。
type Zero = MinusOne<1> // 0
type FiftyFour = MinusOne<55> // 54
答案
type MinusOne =
U['length'] extends T
? U[0]
: MinusOne
// max 999 -> 998 TS最大递归次数
PickByType
Medium
, object
从 F
中选出类型相同的属性
type OnlyBoolean = PickByType<{
name: string
count: number
isReadonly: boolean
isEnable: boolean
}, boolean> // { isReadonly: boolean; isEnable: boolean; }
答案
type PickByType = {
[K in keyof T as T[K] extends U ? K : never]: T[K]
}
OmitByType
Medium
, #object
保留没有在U中指定的类型的字段
type OmitBoolean = OmitByType<{
name: string
count: number
isReadonly: boolean
isEnable: boolean
}, boolean> // { name: string; count: number }
答案
type OmitByType = {
[K in keyof T as T[K] extends U ? never : K] : T[K]
}
StartsWith, EndsWith
Medium
, #template-literal
实现StartsWith
,接收两个string类型参数,然后判断T
是否以U
开头,根据结果返回true
或false
type a = StartsWith<'abc', 'ac'> // expected to be false
type b = StartsWith<'abc', 'ab'> // expected to be true
type c = StartsWith<'abc', 'abcd'> // expected to be false
答案
type StartsWith =
T extends `${U}${string}` ? true : false
type EndsWith =
T extends `${string}${U}` ? true : false
PartialByKeys
Medium
, #object
实现一个通用的PartialByKeys
,它接收两个类型参数T
和K
。
K
指定应设置为可选的T
的属性集。当没有提供K
时,它就和普通的Partial
一样使所有属性都是可选的。
interface User {
name: string
age: number
address: string
}
type UserPartialName = PartialByKeys // { name?:string; age:number; address:string }
答案
type Copy = {
[K in keyof T]:T[K]
}
type PartialByKeys =
Copy>> & Omit>
RequiredByKeys
Medium
, #object
实现一个通用的RequiredByKeys
,它接收两个类型参数T
和K
。
K
指定应设为必选的T
的属性集。当没有提供K
时,它就和普通的Required
一样使所有的属性成为必选的。
interface User {
name?: string
age?: number
address?: string
}
type UserRequiredName = RequiredByKeys // { name: string; age?: number; address?: string }
答案
type Copy = {
[K in keyof T]:T[K]
}
type RequiredByKeys =
Copy>> & Omit>
Mutable
Medium
, #readonly
, object-keys
实现一个通用的类型 Mutable
,使类型 T
的全部属性可变(非只读)。
interface Todo {
readonly title: string
readonly description: string
readonly completed: boolean
}
type MutableTodo = Mutable // { title: string; description: string; completed: boolean; }
答案
type Mutable = {
-readonly[K in keyof T]: T[K]
}
ObjectEntries
Medium
, #object
1
interface Model {
name: string;
age: number;
locations: string[] | null;
}
type modelEntries = ObjectEntries // ['name', string] | ['age', number] | ['locations', string[] | null];
答案
type ObjectEntries = {
[K in keyof T]-?: [
K, Exclude extends never
? undefined
: Exclude
]
}[keyof T]
Tuple to Nested Object
Medium
给定只包含字符串的元组,和类型U, 递归构建对象
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
答案
type TupleToNestedObject = T extends [infer H extends string, ...infer R]
? { [K in H] : TupleToNestedObject }
: U
Reverse
Medium
, #tuple
实现类型版本的数组反转 Array.reverse
type a = Reverse<['a', 'b']> // ['b', 'a']
type b = Reverse<['a', 'b', 'c']> // ['c', 'b', 'a']
答案
type Reverse =
T extends [infer H, ...infer R] ? [...Reverse, H] : []
Flip Arguments
Medium
, #arguments
实现类型版本的 lodash _.flip
函数
类型 FlipArguments
需要函数 T
并返回一个新的函数类型。这个函数类型拥有相同的参数,但参数类型是被反转的。
type Flipped = FlipArguments<(arg0: string, arg1: number, arg2: boolean) => void>
// (arg0: boolean, arg1: number, arg2: string) => void
答案
type Reverse =
T extends [infer H, ...infer R] ? [...Reverse, H] : []
type FlipArguments any> =
T extends (...args: infer P) => infer R
? (...args: Reverse) => R
: void
FlattenDepth
Medium
, #array
按深度递归展平阵列。
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
答案
type FlattenOnce =
T extends [infer H, ...infer R]
? H extends any[]
? [...H, ...FlattenOnce]
: [H, ...FlattenOnce]
: T
type FlattenDepth<
T extends any[],
D extends number = 1,
Count extends 1[] = [],
Flattened extends any[] = Count['length'] extends D
? T
: FlattenOnce,
> = Flattened extends T ? T : FlattenDepth
BEM style string
Medium
块、元素、修饰符方法 (BEM) 是 CSS 中类的流行命名约定。例如,块组件将表示为 btn
,依赖于块的元素将表示为 btn__price
,改变块样式的修饰符将表示为 btn--big
或 btn__price--warning
。实现 BEM 从这三个参数生成字符串联合。其中 B 是字符串文字,E 和 M 是字符串数组(可以为空)。
BEM<'btn', ['price'], ['warning', 'success']
// 'btn__price--warning' | 'btn__price--success'
答案
type BEM =
`${B}${E extends [] ? '' : `__${E[number]}`}${M extends [] ? '' : `--${M[number]}`}`
InorderTraversal
Medium
,#object
实现二叉树中序遍历的类型版本。
const tree1 = {
val: 1,
left: null,
right: {
val: 2,
left: {
val: 3,
left: null,
right: null,
},
right: null,
},
} as const
type A = InorderTraversal // [1, 3, 2]
答案
interface TreeNode {
val: number
left: TreeNode | null
right: TreeNode | null
}
type InorderTraversal =
[T] extends [TreeNode]
? [
...InorderTraversal,
T['val'],
...InorderTraversal
] : []
Flip
Medium
实现类型 just-flip-object
:
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'}
答案
type Flip = {
[
K in keyof T as T[K] extends keyof any
? T[K]
: `${T[K] & (bigint | boolean | null | undefined)}`
]: K
}
Fibonacci Squence
Medium
实现泛型 Fibonacci
传入数字 T
返回正确的 Fibonacci number.
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...
type Result1 = Fibonacci<3> // 2
type Result2 = Fibonacci<8> // 21
答案
type Fibonacci<
T extends number,
N1 extends 1[] = [],
N2 extends 1[] = [1],
Count extends 1[] = [1]
> =
T extends 0
? 0
: Count['length'] extends T
? N2['length']
: Fibonacci
AllCombinations
Medium
实现类型 AllCombinations
返回所有字符组合.
type AllCombinations_ABC = AllCombinations<'ABC'>;
// should be '' | 'A' | 'B' | 'C' | 'AB' | 'AC' | 'BA' | 'BC' | 'CA' | 'CB' | 'ABC' | 'ACB' | 'BAC' | 'BCA' | 'CAB' | 'CBA'
答案
type String2Union =
S extends `${infer C}${infer REST}`
? C | String2Union
: never
type AllCombinations<
STR extends string,
S extends string = String2Union,
> = [S] extends [never]
? ''
: '' | {[K in S]: `${K}${AllCombinations>}`}[S]
Greater Than
Medium
, #array
实现类型 GreaterThan
来比较大小,就像 T > U
。不需要考虑负数
1
GreaterThan<2, 1> //should be true
GreaterThan<1, 1> //should be false
GreaterThan<10, 100> //should be false
GreaterThan<111, 11> //should be true
答案
type GreaterThan<
TA extends number,
TB extends number,
TArray extends unknown[] = [],
TResult extends [boolean, boolean] = [
TA extends TArray['length'] ? true : false,
TB extends TArray['length'] ? true : false
]
> = TA extends TB
? false
: TResult extends [true, false]
? false
: TResult extends [false, true]
? true
: GreaterThan
Zip
Medium
, #tuple
实现 Zip
类型。 T, U 必须为 Tuple
type exp = Zip<[1, 2], [true, false]> // expected to be [[1, true], [2, false]]
答案
type Zip =
T extends [infer TH, ...infer TR]
? U extends [infer UH, ...infer UR]
? [[TH, UH], ...Zip]
: []
: []
IsTuple
Medium
, #tuple
实现 IsTuple
, 接收类型 T
判断 T
是否为元组类型
type case1 = IsTuple<[number]> // true
type case2 = IsTuple // true
type case3 = IsTuple // false
答案
type IsTuple =
[T] extends [never]
? false
: T extends readonly []
? true
: T extends [infer H, ...infer R] | readonly [infer H, ...infer R]
? true
: false
type IsTuple =
[T] extends [never]
? false
: T extends readonly any[]
? number extends T['length']
? false
: true
: false
Chunk
Medium
, #tuple
实现 Chunk
, 它有两个必填的类型参数,T
必须为 tuple
, N
必须为大于1的数字
type exp1 = Chunk<[1, 2, 3], 2> // expected to be [[1, 2], [3]]
type exp2 = Chunk<[1, 2, 3], 4> // expected to be [[1, 2, 3]]
type exp3 = Chunk<[1, 2, 3], 1> // expected to be [[1], [2], [3]]
答案
type Chunk<
T extends any[],
N extends number,
Result extends any[] = [],
Cache extends any[] = []
> = T extends [infer H, ...infer R]
? Cache['length'] extends N
? Chunk
: Chunk
: Cache extends []
? Result
: [...Result, Cache]
Fill
Medium
, tuple
Fill
, 一个常用的 JavaScript 函数, 我们用类型实现它. Fill
, 它接收4个类型参数, T
, N
是必填参数 T
为元组, N
为 any, Start
, End
是可选参数,为大于零的数子.
type exp = Fill<[1, 2, 3], 0> // expected to be [0, 0, 0]
为了模拟真实的功能,测试中可能会包含一些边界条件,希望大家喜欢:)
答案
type Fill<
TArray extends Array,
TN,
TStart extends number = 0,
TEnd extends number = TArray["length"],
TResult extends Array = [],
TCanFill = false
> = EmptyRange extends true
? TArray
: TArray extends [infer First, ...infer Rest]
? TResult["length"] extends TStart
? Fill
: TResult["length"] extends TEnd
? Fill
: Fill
: TResult
Trim Right
Medium
实现 TrimRight
它采用精确的字符串类型并返回一个删除了空格结尾的新字符串。
type Trimed = TrimRight<' Hello World '> // expected to be ' Hello World'
答案
type TrimChar = ' ' | '\n' | '\t'
type TrimRight =
S extends `${infer R}${TrimChar}` ? TrimRight: S
Without
Medium
, #union
, #array
实现一个像 Lodash.without 函数一样的泛型 Without,它接收数组类型的 T 和数字或数组类型的 U 为参数,会返回一个去除 U 中元素的数组 T。
type Res = Without<[1, 2], 1>; // expected to be [2]
type Res1 = Without<[1, 2, 4, 1, 5], [1, 2]>; // expected to be [4, 5]
type Res2 = Without<[2, 3, 2, 3, 2, 3, 2, 3], [2, 3]>; // expected to be []
答案
type Without =
T extends [U extends any[] ? U[number] : U, ...infer R]
? Without
: T extends [infer H, ...infer R]
? [H, ...Without]
: T
Trunc
Medium
, template-literal
实现类型版本的 Math.trunc
. 它接受字符串或数字返回整数部分,提出小数部分
type A = Trunc<12.34> // 12
答案
type Trunc = `${N}` extends `${infer H}.${string}`
? H
: `${N}`
IndexOf
Medium
, #array
实现类型版本的 Array.indexOf
, 它接收数组T 和 U 返回U在T中的索引值
type Res = IndexOf<[1, 2, 3], 2>; // expected to be 1
type Res1 = IndexOf<[2,6, 3,8,4,1,7, 3,9], 3>; // expected to be 2
type Res2 = IndexOf<[0, 0, 0], 2>; // expected to be -1
答案
type IndexOf =
T extends [infer H, ...infer R]
? Equal extends true
? Cache['length']
: IndexOf
: -1
Join
Medium
, #array
实现类型版 Array.join
接收数组T和字符串或数字 U
type Res = Join<["a", "p", "p", "l", "e"], "-">; // expected to be 'a-p-p-l-e'
type Res1 = Join<["Hello", "World"], " ">; // expected to be 'Hello World'
type Res2 = Join<["2", "2", "2"], 1>; // expected to be '21212'
type Res3 = Join<["o"], "u">; // expected to be 'o'
答案
type Join =
T extends [infer H, ...infer R]
? Join
: Result
LastIndexOf
Medium
, #array
实现类型版本的 Array.lastIndexOf
, 它接收数组T 和 U 返回U在T中的反向索引值
type Res1 = LastIndexOf<[1, 2, 3, 2, 1], 2> // 3
type Res2 = LastIndexOf<[0, 0, 0], 2> // -1
答案
type LastIndexOf =
O extends [...infer R, infer T]
? Equal extends true
? R['length']
: LastIndexOf
: -1
Unique
Medium
, #array
实现类型版本的Lodash.uniq
, 它接收数组T,返回去重后的T
type Res = Unique<[1, 1, 2, 2, 3, 3]>; // expected to be [1, 2, 3]
type Res1 = Unique<[1, 2, 3, 4, 4, 5, 6, 7]>; // expected to be [1, 2, 3, 4, 5, 6, 7]
type Res2 = Unique<[1, "a", 2, "b", 2, "a"]>; // expected to be [1, "a", 2, "b"]
type Res3 = Unique<[string, number, 1, "a", 1, string, 2, "b", 2, number]>; // expected to be [string, number, 1, "a", 2, "b"]
type Res4 = Unique<[unknown, unknown, any, any, never, never]>; // expected to be [unknown, any, never]
答案
type IsIncludes = T extends [infer F, ...infer R]
? Equal extends true
? true
: IsIncludes
: false
type Unique =
T extends [infer H, ...infer R]
? IsIncludes extends false
? Unique
: Unique
: U
MapTypes
Medium
实现 MapTypes
它将对象 T 中的类型转换为类型 R 定义的不同类型,类型 R 具有以下结构。
type StringToNumber = {
mapFrom: string; // value of key which value is string
mapTo: number; // will be transformed for number
}
type StringToNumber = { mapFrom: string; mapTo: number;}
type a = MapTypes<{iWillBeANumberOneDay: string}, StringToNumber> // gives { iWillBeANumberOneDay: number; }
type StringToDate = { mapFrom: string; mapTo: Date;}
type b = MapTypes<{iWillBeNumberOrDate: string}, StringToDate | StringToNumber> // gives { iWillBeNumberOrDate: number | Date; }
type c = MapTypes<{iWillBeANumberOneDay: string, iWillStayTheSame: Function}, StringToNumber> // // gives { iWillBeANumberOneDay: number, iWillStayTheSame: Function }
答案
type GetMapToType<
T,
R,
Type = R extends { mapFrom: T; mapTo: infer To } ? To : never
> = [Type] extends [never] ? T : Type
type MapTypes = {
[key in keyof T]: GetMapToType
}
Construct Tuple
Medium
, #tuple
构造一个给定长度的元组
type result = ConstructTuple<2> // expect to be [unknown, unkonwn]
答案
type ConstructTuple =
L extends Res['length']
? Res
: ConstructTuple
Number Range
Medium
有时我们想限制数字的范围......例如。
type result = NumberRange<2 , 9> // | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
答案
type NumberRange<
T extends number,
U extends number,
R extends any[] = [],
L extends any[] = [],
S extends any[] = []
> = T extends R['length']
? U extends L['length']
? S[number] | L['length']
: NumberRange
: NumberRange
Combination
Medium
, #array
, #application
, #string
给定一个字符串数组,进行置换和组合。它对于像video controlsList这样的类型也很有用
// expected to be `"foo" | "bar" | "baz" | "foo bar" | "foo bar baz" | "foo baz" | "foo baz bar" | "bar foo" | "bar foo baz" | "bar baz" | "bar baz foo" | "baz foo" | "baz foo bar" | "baz bar" | "baz bar foo"`
type Keys = Combination<['foo', 'bar', 'baz']>
答案
type Combination =
U extends infer U extends string
? `${U} ${Combination>}` | U
: never
Subsequence
Medium
, #union
给定一个唯一元素数组,返回所有可能的子序列。
子序列是一个序列,可以通过删除一些元素或不删除任何元素而从数组中派生,而不改变其余元素的顺序。
type A = Subsequence<[1, 2]> // [] | [1] | [2] | [1, 2]
答案
type Subsequence = T extends [infer X, ...infer Y]
? [X, ...Subsequence] | Subsequence
: []
你可能感兴趣的:(TypeScript 类型挑战 Medium)