Skip to content
索引

type-challenges

学习typscript的开源仓库

地址:https://github.com/type-challenges/type-challenges

warm-up

下方题目列表为热身题,总数量为1

13.helloworld

Hello, World!

在Type Challenges,我们使用类型系统自身来实现自动判题`

在这个挑战中,你需要修改下方的代码使得测试通过(没有类型错误)

ts
// 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>>

解答

ts
type HelloWorld = string
type HelloWorld = string

easy

下方题目列表为简单题,总数量为13

4.pick

实现 TS 内置的 Pick<T, K>,但不可以使用它

从类型 T 中选择出属性 K,构造成一个新的类型

举个例子

ts
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,
}

解答

ts
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 所修饰

也就是不可以再对该对象的属性赋值

例如:

ts
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

解答

ts
type MyReadonly<T> = {
  readonly [K in keyof T] : T[K]
}
type MyReadonly<T> = {
  readonly [K in keyof T] : T[K]
}

11.Tuple to Object

传入一个元组类型,将这个元组类型转换为对象类型,这个对象类型的键/值都是从元组中遍历出来

例如:

ts
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'}

解答

ts
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并返回它的第一个元素的类型。

例如:

ts
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

解答

ts
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的数组,返回这个数组的长度

例如:

ts
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

解答

ts
type Length<T extends readonly any[]> = T['length'] 
type Length<T extends readonly any[]> = T['length'] 

43.Exclude

实现内置的Exclude <T, U>类型,但不能直接使用它本身。

从联合类型T中排除U的类型成员,来构造一个新的类型

解答

ts
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 类型

解答

ts
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,返回正确的值

js
type If<C extends boolean, T, F> = C extends true ? T : F
type If<C extends boolean, T, F> = C extends true ? T : F
js
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 boolean true or false

在类型系统中实现JavaScript的Array.includes 函数,一个包含两个参数的类型,返回的类型是truefalse

示例

ts
type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false`
type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false`

测试用例

ts
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>>,
]

解答

ts
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 Firstinfer Rest分别占位数组的第一个和数组的剩余元素,

Equal<First, U> extends true表示数组第一个元素和第二个传入的参数相同的情况下,返回true,否则使用递归方式来继续执行,从而遍历数组的元素

3057.Push

Implement the type version of Array.push

实现类型版本的 Array.push

示例

ts
type Result = Push<[1, 2], '3'> // [1, 2, '3']
type Result = Push<[1, 2], '3'> // [1, 2, '3']

测试用例

ts
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]>>
]

解答

ts
type Push<T extends any[], U> = [...T, U]
type Push<T extends any[], U> = [...T, U]

总结

T extends any[]表示限制传入的第一个类型是一个任意的数组类型

3060.Unshift

实现类型版本的 Array.unshift

示例

ts
type Result = Unshift<[1, 2], 0> // [0, 1, 2]
type Result = Unshift<[1, 2], 0> // [0, 1, 2]

测试用例

ts
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"]>>
]

解答

ts
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泛型,而不是使用它

测试用例

ts
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>, []>>
]

解答

ts
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,从而返回参数的数组类型

Released under the MIT License.