ts类型挑战【八】

116 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情

题目十一:parameters

// template.ts
type MyParameters<T extends (...args: any[]) => any> = any
// test-cases.ts
import { Equal, Expect, ExpectFalse, NotEqual } 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>, []>>,
]

这道题目要求我们实现内置的 Parameters 类型,而不是直接使用它,可参考TypeScript官方文档

TS 官网对于 Parameters 类型的介绍:从函数类型的参数中使用的类型构造元组类型。

也就是获取函数的参数以及其类型

  • 示例1
type T0 = Parameters<() => string>; // type T0 = []

因为 () => string 这个函数的参数为空,所以返回空数组: []

  • 示例2
type T1 = Parameters<(s: string) => void>; // type T1 = [s: string]

(s: string) => void:该函数只有一个参数 s,并且参数类型为 string,所以返回值为:[s: string]

  • 示例3
type T2 = Parameters<<T>(arg: T) => T>; // type T2 = [arg: unknown]

我们知道在 TS 类型中,T 代表任意类型的其中一种,所以这里的返回的 arg 的类型为 unknown

  • 示例4
declare function f1(arg: { a: number; b: string }): void;
type T3 = Parameters<typeof f1>; // type T3 = [arg: { a: number; b: string; }]

有了上面的示例,这里就很好理解:函数参数 arg 是一个对象,里面有 ab 两个属性(其中 typeof 负责将函数转换为相应的函数类型

  • 示例5
type T4 = Parameters<any>; // type T4 = unknown[]
  • 示例6
type T5 = Parameters<never>; // type T5 = never
type T6 = Parameters<string>; // type T6 = never
type T7 = Parameters<Function>; // type T7 = never

其中 T6T7 会报错,因为其不满足传入的函数类型约束

代码实现

  • Parameters 的入参应该是一个携带参数的函数定义(不能是Function):() => any
// 这里其实题目中已经有了,注意 T 是一个函数,args是其参数数组
type MyParameters<T extends (...args: any[]) => any> = any
  • 拿到 ...args 的类型,直接返回即可

我们知道在 ts 类型中,想定义参数的话,需要使用 infer,而 infer 只能在等号右侧定义

type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any

现在,我们已经拿到入参(args)定义的类型 P 了,那么只需要将 P 返回即可;不满足约束(extends)的返回 never

type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never

至此,这道题就解决了。

总结:

  • 入参限制类型:() => any,不满足的直接返回 never
  • 使用 infer 在等号右侧定义入参(args)的类型 P
    • 满足条件则返回 P
    • 否则返回 never