type-challenges
学习typscript的开源仓库
warm-up
下方题目列表为
热身
题,总数量为1
13.helloworld
Hello, World!
在Type Challenges,我们使用类型系统自身来实现自动判题`
在这个挑战中,你需要修改下方的代码使得测试通过(没有类型错误)
// expected to be string
type HelloWorld = any
// you should make this work
type test = Expect<Equal<HelloWorld, string>>
// expected to be string
type HelloWorld = any
// you should make this work
type test = Expect<Equal<HelloWorld, string>>
解答
type HelloWorld = string
type HelloWorld = string
easy
下方题目列表为
简单
题,总数量为13
4.pick
实现 TS 内置的 Pick<T, K>
,但不可以使用它
从类型 T
中选择出属性 K
,构造成一个新的类型
举个例子
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyPick<Todo, 'title' | 'completed'>
const todo: TodoPreview = {
title: 'Clean room',
completed: false,
}
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyPick<Todo, 'title' | 'completed'>
const todo: TodoPreview = {
title: 'Clean room',
completed: false,
}
解答
type MyPick<T, K extends keyof T> = {
[key in K]: T[key]
}
type MyPick<T, K extends keyof T> = {
[key in K]: T[key]
}
7.Readonly
不要使用内置的Readonly<T>
,自己实现一个
该 Readonly
会接收一个 泛型参数,并返回一个完全一样的类型,只是所有属性都会被 readonly
所修饰
也就是不可以再对该对象的属性赋值
例如:
interface Todo {
title: string
description: string
}
const todo: MyReadonly<Todo> = {
title: "Hey",
description: "foobar"
}
todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property
interface Todo {
title: string
description: string
}
const todo: MyReadonly<Todo> = {
title: "Hey",
description: "foobar"
}
todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property
解答
type MyReadonly<T> = {
readonly [K in keyof T] : T[K]
}
type MyReadonly<T> = {
readonly [K in keyof T] : T[K]
}
11.Tuple to Object
传入一个元组类型,将这个元组类型转换为对象类型,这个对象类型的键/值都是从元组中遍历出来
例如:
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const
type result = TupleToObject<typeof tuple> // expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const
type result = TupleToObject<typeof tuple> // expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}
解答
type TupleToObject<T extends readonly any[]> = {
[P in T[number]]: P
}
type TupleToObject<T extends readonly any[]> = {
[P in T[number]]: P
}
14.First of Array
实现一个通用First<T>
,它接受一个数组T
并返回它的第一个元素的类型。
例如:
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
type head1 = First<arr1> // expected to be 'a'
type head2 = First<arr2> // expected to be 3
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
type head1 = First<arr1> // expected to be 'a'
type head2 = First<arr2> // expected to be 3
解答
type First<T extends any[]> = T extends [] ? never : T[0]
type First<T extends any[]> = T['length'] extends 0 ? never : T[0]
type First<T extends any[]> = T[0] extends T[number] ? T[0] : never
type First<T extends any[]> = T extends [infer First, ...unknown[]] ? First : never
type First<T extends any[]> = T extends [infer First,...infer Other] ? First : never
type First<T extends any[]> = T extends [] ? never : T[0]
type First<T extends any[]> = T['length'] extends 0 ? never : T[0]
type First<T extends any[]> = T[0] extends T[number] ? T[0] : never
type First<T extends any[]> = T extends [infer First, ...unknown[]] ? First : never
type First<T extends any[]> = T extends [infer First,...infer Other] ? First : never
18.Length of Tuple
创建一个通用的Length
,接受一个readonly
的数组,返回这个数组的长度
例如:
type tesla = ['tesla', 'model 3', 'model X', 'model Y']
type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT']
type teslaLength = Length<tesla> // expected 4
type spaceXLength = Length<spaceX> // expected 5
type tesla = ['tesla', 'model 3', 'model X', 'model Y']
type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT']
type teslaLength = Length<tesla> // expected 4
type spaceXLength = Length<spaceX> // expected 5
解答
type Length<T extends readonly any[]> = T['length']
type Length<T extends readonly any[]> = T['length']
43.Exclude
实现内置的Exclude <T, U>类型,但不能直接使用它本身。
从联合类型T中排除U的类型成员,来构造一个新的类型
解答
type MyExclude<T, U> = T extends U ? never : T
type MyExclude<T, U> = T extends U ? never : T
189.Awaited
假如我们有一个 Promise 对象,这个 Promise 对象会返回一个类型。在 TS 中,我们用 Promise 中的 T 来描述这个 Promise 返回的类型。请你实现一个类型,可以获取这个类型。
比如:Promise<ExampleType>
,请你返回 ExampleType 类型
解答
type MyAwaited<T extends Promise<unknown>> = T extends Promise<infer X> ? (X extends Promise<unknown> ? MyAwaited<X> : X) : never
type MyAwaited<T extends Promise<unknown>> = T extends Promise<infer X> ? (X extends Promise<unknown> ? MyAwaited<X> : X) : never
268.if
限制C的类型为boolean,然后判断C extends true 还是false,返回正确的值
type If<C extends boolean, T, F> = C extends true ? T : F
type If<C extends boolean, T, F> = C extends true ? T : F
type If<C extends boolean, T, F> = C extends true ? T : F
// 结果:'a'
type result1 = If<true, "a", "b">
// 结果:'b'
type result2 = If<false, "a", "b">
type If<C extends boolean, T, F> = C extends true ? T : F
// 结果:'a'
type result1 = If<true, "a", "b">
// 结果:'b'
type result2 = If<false, "a", "b">
3057.Includes
Implement the JavaScript
Array.includes
function in the type system.A type takes the two arguments.The output should be a booleantrue
orfalse
在类型系统中实现JavaScript的
Array.includes
函数,一个包含两个参数的类型,返回的类型是true
或false
。
示例
type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false`
type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false`
测试用例
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Kars'>, true>>,
Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'>, false>>,
Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 7>, true>>,
Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 4>, false>>,
Expect<Equal<Includes<[1, 2, 3], 2>, true>>,
Expect<Equal<Includes<[1, 2, 3], 1>, true>>,
Expect<Equal<Includes<[{}], { a: 'A' }>, false>>,
Expect<Equal<Includes<[boolean, 2, 3, 5, 6, 7], false>, false>>,
Expect<Equal<Includes<[true, 2, 3, 5, 6, 7], boolean>, false>>,
Expect<Equal<Includes<[false, 2, 3, 5, 6, 7], false>, true>>,
Expect<Equal<Includes<[{ a: 'A' }], { readonly a: 'A' }>, false>>,
Expect<Equal<Includes<[{ readonly a: 'A' }], { a: 'A' }>, false>>,
Expect<Equal<Includes<[1], 1 | 2>, false>>,
Expect<Equal<Includes<[1 | 2], 1>, false>>,
Expect<Equal<Includes<[null], undefined>, false>>,
Expect<Equal<Includes<[undefined], null>, false>>,
]
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Kars'>, true>>,
Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'>, false>>,
Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 7>, true>>,
Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 4>, false>>,
Expect<Equal<Includes<[1, 2, 3], 2>, true>>,
Expect<Equal<Includes<[1, 2, 3], 1>, true>>,
Expect<Equal<Includes<[{}], { a: 'A' }>, false>>,
Expect<Equal<Includes<[boolean, 2, 3, 5, 6, 7], false>, false>>,
Expect<Equal<Includes<[true, 2, 3, 5, 6, 7], boolean>, false>>,
Expect<Equal<Includes<[false, 2, 3, 5, 6, 7], false>, true>>,
Expect<Equal<Includes<[{ a: 'A' }], { readonly a: 'A' }>, false>>,
Expect<Equal<Includes<[{ readonly a: 'A' }], { a: 'A' }>, false>>,
Expect<Equal<Includes<[1], 1 | 2>, false>>,
Expect<Equal<Includes<[1 | 2], 1>, false>>,
Expect<Equal<Includes<[null], undefined>, false>>,
Expect<Equal<Includes<[undefined], null>, false>>,
]
解答
type Includes<T extends readonly any[], U> = T extends [infer First, ...infer Rest]
? Equal<First, U> extends true
? true
: Includes<Rest, U>
: false
type Includes<T extends readonly any[], U> = T extends [infer First, ...infer Rest]
? Equal<First, U> extends true
? true
: Includes<Rest, U>
: false
总结
T extends [infer First, ...infer Rest]
中infer First
和infer Rest
分别占位数组的第一个和数组的剩余元素,
Equal<First, U> extends true
表示数组第一个元素和第二个传入的参数相同的情况下,返回true
,否则使用递归方式来继续执行,从而遍历数组的元素
3057.Push
Implement the type version of
Array.push
实现类型版本的
Array.push
示例
type Result = Push<[1, 2], '3'> // [1, 2, '3']
type Result = Push<[1, 2], '3'> // [1, 2, '3']
测试用例
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<Push<[], 1>, [1]>>,
Expect<Equal<Push<[1, 2], '3'>, [1, 2, '3']>>,
Expect<Equal<Push<['1', 2, '3'], boolean>, ['1', 2, '3', boolean]>>
]
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<Push<[], 1>, [1]>>,
Expect<Equal<Push<[1, 2], '3'>, [1, 2, '3']>>,
Expect<Equal<Push<['1', 2, '3'], boolean>, ['1', 2, '3', boolean]>>
]
解答
type Push<T extends any[], U> = [...T, U]
type Push<T extends any[], U> = [...T, U]
总结
T extends any[]
表示限制传入的第一个类型是一个任意的数组类型
3060.Unshift
实现类型版本的
Array.unshift
示例
type Result = Unshift<[1, 2], 0> // [0, 1, 2]
type Result = Unshift<[1, 2], 0> // [0, 1, 2]
测试用例
import type { Equal, Expect } from "@type-challenges/utils"
type cases = [
Expect<Equal<Unshift<[], 1>, [1]>>,
Expect<Equal<Unshift<[1, 2], 0>, [0, 1, 2]>>,
Expect<Equal<Unshift<["1", 2, "3"], boolean>, [boolean, "1", 2, "3"]>>
]
import type { Equal, Expect } from "@type-challenges/utils"
type cases = [
Expect<Equal<Unshift<[], 1>, [1]>>,
Expect<Equal<Unshift<[1, 2], 0>, [0, 1, 2]>>,
Expect<Equal<Unshift<["1", 2, "3"], boolean>, [boolean, "1", 2, "3"]>>
]
解答
type Unshift<T extends any[], U> = [U, ...T]
type Unshift<T extends any[], U> = [U, ...T]
3312.Parameters
Implement the built-in Parameters generic without using it
实现内置的Parameters泛型,而不是使用它
测试用例
import type { Equal, Expect } from "@type-challenges/utils"
const foo = (arg1: string, arg2: number): void => {}
const bar = (arg1: boolean, arg2: { a: "A" }): void => {}
const baz = (): void => {}
type cases = [
Expect<Equal<MyParameters<typeof foo>, [string, number]>>,
Expect<Equal<MyParameters<typeof bar>, [boolean, { a: "A" }]>>,
Expect<Equal<MyParameters<typeof baz>, []>>
]
import type { Equal, Expect } from "@type-challenges/utils"
const foo = (arg1: string, arg2: number): void => {}
const bar = (arg1: boolean, arg2: { a: "A" }): void => {}
const baz = (): void => {}
type cases = [
Expect<Equal<MyParameters<typeof foo>, [string, number]>>,
Expect<Equal<MyParameters<typeof bar>, [boolean, { a: "A" }]>>,
Expect<Equal<MyParameters<typeof baz>, []>>
]
解答
type MyParameters<T extends (...args: any) => any> = T extends (...args: infer R) => any
? [...R]
: any
type MyParameters<T extends (...args: any) => any> = T extends (...args: infer R) => any
? [...R]
: any
总结
(...args: any) => any
是一个整体,表示一个参数任意,返回值任意的函数类型
T extends (...args: any) => any
表示限制传入的类型必须是一个函数类型
T extends (...args: infer R) => any
其中的infer R
用来占位,这里的R
表示的就是参数的类型,之后就可以通过...
展开运算符来展开R
,从而返回参数的数组类型