发现一个好玩的开源项目:type-challenges,在上面可以做一些TypeScript类型相关的题目,这里记录一下自己的学习。
/* _____________ Your Code Here _____________ */
type MyExclude<T, U> = T extends U ? never : T
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<MyExclude<'a' | 'b' | 'c', 'a'>, Exclude<'a' | 'b' | 'c', 'a'>>>,
Expect<Equal<MyExclude<'a' | 'b' | 'c', 'a' | 'b'>, Exclude<'a' | 'b' | 'c', 'a' | 'b'>>>,
Expect<Equal<MyExclude<string | number | (() => void), Function>, Exclude<string | number | (() => void), Function>>>,
]
这里最神奇的是extends的用法,可以理解为,对于 T extends U
,如果T是一个单一类型,那么就判断T
是不是extends U
,如果T
是一个复合类型,就依次判断T
中每一个元素是不是extends U
/* _____________ Your Code Here _____________ */
type MyAwaitedR<T> = T extends Promise<infer K> ? MyAwaitedR<K> : T
type MyAwaited<T> = T extends Promise<infer K> ? MyAwaitedR<K> : error
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type X = Promise<string>
type Y = Promise<{ field: number }>
type Z = Promise<Promise<string | number>>
type cases = [
Expect<Equal<MyAwaited<X>, string>>,
Expect<Equal<MyAwaited<Y>, { field: number }>>,
Expect<Equal<MyAwaited<Z>, string | number>>,
]
// @ts-expect-error
type error = MyAwaited<number>
这里的关键点是:
type MyAwaited<T> = T extends Promise<infer R> ? MyAwaited<R> : T
,但是这样不满足最后一个error的判断条件,因为MyAwaited<number>
返回的是 number
不是error
。type MyAwaited<T> = T extends Promise<infer R> ? R extends Promise<infer M> ? M : R : error
这样确实可以满足题目中所有的情况,但是如果把type Z = Promise<Promise<string | number>>
改成type Z = Promise<Promise<Promise<string | number>>>
就不行了,所以还是得用递归来实现。(这里无限套娃Promise
应该是可以的吧?)/* _____________ Your Code Here _____________ */
type If<C, T, F> = C extends true ? T : C extends false ? F : error
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<If<true, 'a', 'b'>, 'a'>>,
Expect<Equal<If<false, 'a', 2>, 2>>,
]
// @ts-expect-error
type error = If<null, 'a', 'b'>
/* _____________ Your Code Here _____________ */
type Concat<T extends readonly any[], U extends readonly any[]> = [...T, ...U]
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<Concat<[], []>, []>>,
Expect<Equal<Concat<[], [1]>, [1]>>,
Expect<Equal<Concat<[1, 2], [3, 4]>, [1, 2, 3, 4]>>,
Expect<Equal<Concat<['1', 2, '3'], [false, boolean, '4']>, ['1', 2, '3', false, boolean, '4']>>,
]
/* _____________ Your Code Here _____________ */
type Includes<T extends readonly any[], U> = T extends [infer X, ...infer Y] ? (Equal<X, U> extends true ? true: Includes<Y,U>) : false
/* _____________ Test Cases _____________ */
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>>,
]
迷一样的898,我真搞不懂,这道题怎么会被分到简单里面的?答案参考的这里
但是我觉得这个方法也不是很完美,因为Equal
这个东西是"@type-challenges/utils"
里面的东西,不是TS自带的。
一开始,我是就是很简单得这么实现的:
后来参考了大佬的答案,我想实现一个自己的Equal函数,结果有两条过不去,经过测试,面对readonly属性有点无能为力
单独研究一下这个Equal是怎么实现的:
export type Equal<X, Y> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? true : false
说实话也没怎么看懂,网上有个专门讨论这个问题的帖子
但是这个Equal也不完美,他有个问题如下:
理论上 { a: string } & { b: string }
和{ a: string; b: string }
这两个应该是相等的,但是这个Equal居然判断不相等。
这道题就先过吧。
/* _____________ Your Code Here _____________ */
type Push<T extends any[], U> = [...T, U]
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
import { ExpectFalse, NotEqual } 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]>>,
]
/* _____________ Your Code Here _____________ */
type Unshift<T extends any[], U> = [U, ...T]
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
import { ExpectFalse, NotEqual } 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 MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer R) => any ? R : never
/* _____________ Test Cases _____________ */
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>, []>>,
]
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。