发现一个好玩的开源项目:type-challenges,在上面可以做一些TypeScript类型相关的题目,这里记录一下自己的学习。
00002-medium-return-type
代码语言:typescript复制/* _____________ Your Code Here _____________ */
type MyReturnType<T> = T extends (...params: any) => infer R ? R : never
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<string, MyReturnType<() => string>>>,
Expect<Equal<123, MyReturnType<() => 123>>>,
Expect<Equal<ComplexObject, MyReturnType<() => ComplexObject>>>,
Expect<Equal<Promise<boolean>, MyReturnType<() => Promise<boolean>>>>,
Expect<Equal<() => 'foo', MyReturnType<() => () => 'foo'>>>,
Expect<Equal<1 | 2, MyReturnType<typeof fn>>>,
Expect<Equal<1 | 2, MyReturnType<typeof fn1>>>,
]
type ComplexObject = {
a: [12, 'foo']
bar: 'hello'
prev(): number
}
const fn = (v: boolean) => v ? 1 : 2
const fn1 = (v: boolean, w: any) => v ? 1 : 2
00003-medium-omit
代码语言:typescript复制/* _____________ Your Code Here _____________ */
type MyOmit<T, K extends keyof T> = { [R in Exclude<keyof T, K>]: T[R] }
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<Expected1, MyOmit<Todo, 'description'>>>,
Expect<Equal<Expected2, MyOmit<Todo, 'description' | 'completed'>>>,
]
// @ts-expect-error
type error = MyOmit<Todo, 'description' | 'invalid'>
interface Todo {
title: string
description: string
completed: boolean
}
interface Expected1 {
title: string
completed: boolean
}
interface Expected2 {
title: string
}
需要说明的是,这里的MyOmit
和TS中提供的Omit
是不一样的,TS中的版本定义为:type Omit<T, K extends string | number | symbol> = { [P in Exclude<keyof T, K>]: T[P]; }
,不强制要求 K
在 key of T
中存在,但是这道题需要声明K extends keyof T
以满足type error = MyOmit<Todo, 'description' | 'invalid'>
需要报错的测试用例
00008-medium-readonly-2
代码语言:typescript复制/* _____________ Your Code Here _____________ */
type MyReadonly2<T, K extends keyof T = keyof T> = { [R in Exclude<keyof T, K>]: T[R] } & { readonly [R in K]: T[R] }
/* _____________ Test Cases _____________ */
import type { Alike, Expect } from '@type-challenges/utils'
type cases = [
Expect<Alike<MyReadonly2<Todo1>, Readonly<Todo1>>>,
Expect<Alike<MyReadonly2<Todo1, 'title' | 'description'>, Expected>>,
Expect<Alike<MyReadonly2<Todo2, 'title' | 'description'>, Expected>>,
]
interface Todo1 {
title: string
description?: string
completed: boolean
}
interface Todo2 {
readonly title: string
description?: string
completed: boolean
}
interface Expected {
readonly title: string
readonly description?: string
completed: boolean
}
这里最神奇的地方是:MyReadonly2<T, K extends keyof T = keyof T>
中的K extends keyof T = keyof T
他的意思是给K提供一个默认值,如果没有提供K,就使用默认值keyof T
,真的是太神奇了。
00009-medium-deep-readonly
代码语言:typescript复制/* _____________ Your Code Here _____________ */
type DeepReadonly<T> = { readonly [K in keyof T]: keyof T[K] extends never ? T[K] : DeepReadonly<T[K]> }
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<DeepReadonly<X>, Expected>>,
]
type X = {
a: () => 22
b: string
c: {
d: boolean
e: {
g: {
h: {
i: true
j: 'string'
}
k: 'hello'
}
l: [
'hi',
{
m: ['hey']
},
]
}
}
}
type Expected = {
readonly a: () => 22
readonly b: string
readonly c: {
readonly d: boolean
readonly e: {
readonly g: {
readonly h: {
readonly i: true
readonly j: 'string'
}
readonly k: 'hello'
}
readonly l: readonly [
'hi',
{
readonly m: readonly ['hey']
},
]
}
}
}
说实话这个地方我不太理解,有些很复杂的我觉得他是对的,但是太复杂了,也没搞懂。
00012-medium-chainable-options
代码语言:typescript复制/* _____________ Your Code Here _____________ */
type Chainable<T extends object = {}> = {
option<K extends string, V extends unknown>(key: K extends keyof T ? never : K, value: V): Chainable<T & { [P in K]: V }>
get(): T
}
/* _____________ Test Cases _____________ */
import type { Alike, Expect } from '@type-challenges/utils'
declare const a: Chainable
const result1 = a
.option('foo', 123)
.option('bar', { value: 'Hello World' })
.option('name', 'type-challenges')
.get()
const result2 = a
.option('name', 'another name')
// @ts-expect-error
.option('name', 'last name')
.get()
type cases = [
Expect<Alike<typeof result1, Expected1>>,
Expect<Alike<typeof result2, Expected2>>,
]
type Expected1 = {
foo: number
bar: {
value: string
}
name: string
}
type Expected2 = {
name: string
}
话说这道题就离谱,还能这么搞的?简直大开眼界!有个知识点是:
大概意思是:如果T直接是一个Key
, 就直接 {[p in T]: true}
,而不是:{[p in keyof T]: true}
00010-medium-tuple-to-union
代码语言:typescript复制/* _____________ Your Code Here _____________ */
type TupleToUnion<T extends any[]> = T[number]
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<TupleToUnion<[123, '456', true]>, 123 | '456' | true>>,
Expect<Equal<TupleToUnion<[123]>, 123>>,
]
00015-medium-last
代码语言:typescript复制/* _____________ Your Code Here _____________ */
type Last<T extends any[]> = T extends [...any[], infer res] ? res : never
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<Last<[3, 2, 1]>, 1>>,
Expect<Equal<Last<[() => 123, { a: string }]>, { a: string }>>,
]
00016-medium-pop
代码语言:typescript复制/* _____________ Your Code Here _____________ */
type Pop<T extends any[]> = T extends [... infer res, any] ? res : never
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<Pop<[3, 2, 1]>, [3, 2]>>,
Expect<Equal<Pop<['a', 'b', 'c', 'd']>, ['a', 'b', 'c']>>,
]
00015、00016是相似的,不过00016的写法我倒是之前没有想到会这样的。