Appearance
中等
2 - 获取函数返回类型
ts
/* _____________ 你的代码 _____________ */
type MyReturnType<T extends (...args: never[]) => any> = T extends (...args: never[]) => infer A ? A : never
/* _____________ 测试用例 _____________ */
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
/* _____________ 下一步 _____________ */
/*
> 分享你的解答:https://tsch.js.org/2/answer/zh-CN
> 查看解答:https://tsch.js.org/2/solutions
> 更多题目:https://tsch.js.org/zh-CN
*/
3 - 实现 Omit
ts
/* _____________ 你的代码 _____________ */
// type MyOmit<T, K extends keyof T> = { [P in Exclude<keyof T, K>]: T[P] }
type MyOmit<T, K extends keyof T> = {
[P in keyof T as P extends K ? never : P]: T[P]
}
/* _____________ 测试用例 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<Expected1, MyOmit<Todo, 'description'>>>,
Expect<Equal<Expected2, MyOmit<Todo, 'description' | 'completed'>>>,
Expect<Equal<Expected3, MyOmit<Todo1, 'description' | 'completed'>>>,
]
// @ts-expect-error
type error = MyOmit<Todo, 'description' | 'invalid'>
interface Todo {
title: string
description: string
completed: boolean
}
interface Todo1 {
readonly title: string
description: string
completed: boolean
}
interface Expected1 {
title: string
completed: boolean
}
interface Expected2 {
title: string
}
interface Expected3 {
readonly title: string
}
/* _____________ 下一步 _____________ */
/*
> 分享你的解答:https://tsch.js.org/3/answer/zh-CN
> 查看解答:https://tsch.js.org/3/solutions
> 更多题目:https://tsch.js.org/zh-CN
*/
思考: why use as?
Without the as keyword, the keys would be interpreted as plain property keys, and the conditional type exclusion based on P extends K would not work as expected, so it's main goal is to map the keys of the index signature.
https://github.com/type-challenges/type-challenges/issues/448#issuecomment-1875157558
8 - 对象部分属性只读
ts
/* _____________ 你的代码 _____________ */
type MyReadonly2<T, K extends keyof T = keyof T> = { readonly [key in K]: T[key] } & Omit<T, K>
/* _____________ 测试用例 _____________ */
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>>,
Expect<Alike<MyReadonly2<Todo2, 'description'>, Expected>>,
]
// @ts-expect-error
type error = MyReadonly2<Todo1, 'title' | 'invalid'>
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
}
/* _____________ 下一步 _____________ */
/*
> 分享你的解答:https://tsch.js.org/8/answer/zh-CN
> 查看解答:https://tsch.js.org/8/solutions
> 更多题目:https://tsch.js.org/zh-CN
*/
9 - 对象属性只读(递归)
ts
/* _____________ 你的代码 _____________ */
type DeepReadonly<T> = { readonly [key in keyof T]: keyof T[key] extends never ? T[key] : DeepReadonly<T[key]> }
/* _____________ 测试用例 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<DeepReadonly<X1>, Expected1>>,
Expect<Equal<DeepReadonly<X2>, Expected2>>,
]
type X1 = {
a: () => 22
b: string
c: {
d: boolean
e: {
g: {
h: {
i: true
j: 'string'
}
k: 'hello'
}
l: [
'hi',
{
m: ['hey']
},
]
}
}
}
type X2 = { a: string } | { b: number }
type Expected1 = {
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']
},
]
}
}
}
type Expected2 = { readonly a: string } | { readonly b: number }
思考: 怎么判断类型是不是对象类型?
对象类型:如果 T 是一个对象类型,例如 { name: string, age: number },那么 keyof T 将是对象的键组成的联合类型。在这个例子中,keyof T 会是 'name' | 'age'。
非对象类型:如果 T 是基本类型,如 number、string,那么它没有键,因此 keyof T 将是 never。
因此,当 keyof T 结果是 never 时,说明 T 不是对象类型。
10 - 元组转合集
ts
/* _____________ 你的代码 _____________ */
// answer1
// type TupleToUnion<T extends Array<unknown>> = T[number]
// answer2
type TupleToUnion<T> = T extends Array<infer ITEMS> ? ITEMS : never
/* _____________ 测试用例 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<TupleToUnion<[123, '456', true]>, 123 | '456' | true>>,
Expect<Equal<TupleToUnion<[123]>, 123>>,
]
/* _____________ 下一步 _____________ */
/*
> 分享你的解答:https://tsch.js.org/10/answer/zh-CN
> 查看解答:https://tsch.js.org/10/solutions
> 更多题目:https://tsch.js.org/zh-CN
*/