awesome-typescript icon indicating copy to clipboard operation
awesome-typescript copied to clipboard

「重学TS 2.0 」TS 练习题第五题

Open semlinker opened this issue 4 years ago • 24 comments

第五题

定义一个工具类型 AppendArgument,为已有的函数类型增加指定类型的参数,新增的参数名是 x,将作为新函数类型的第一个参数。具体的使用示例如下所示:

type Fn = (a: number, b: string) => number
type AppendArgument<F, A> = // 你的实现代码

type FinalFn = AppendArgument<Fn, boolean> 
// (x: boolean, a: number, b: string) => number

请在下面评论你的答案

semlinker avatar Sep 14 '21 15:09 semlinker

5.1 使用 Parameters 和 ReturnType 工具类型

type AppendArgument<F extends (...args: any) => any, A> 
  = (x: A, ...args: Parameters<F>) => ReturnType<F> 

type Fn = (a: number, b: string) => number
type FinalF = AppendArgument<Fn, boolean> 
// (x: boolean, a: number, b: string) => number

延伸阅读:掌握 TS 这些工具类型,让你开发事半功倍

5.2 使用 infer 方式

type AppendArgument<F, T> = F extends (...args: infer Args) => infer Return ? 
  (x: T, ...args: Args) => Return : never

type Fn = (a: number, b: string) => number
type FinalFn = AppendArgument<Fn, boolean>
// (x: boolean, a: number, b: string) => number

延伸阅读:用上这几招,轻松实现 TS 类型提取

semlinker avatar Sep 16 '21 01:09 semlinker

Parameters 和 ReturnType 也是使用 infer 实现

moronghui avatar Sep 18 '21 08:09 moronghui

Parameters 和 ReturnType 也是使用 infer 实现 对滴,infer 还是蛮有用的,后面的挺多题目也都有用到它。

semlinker avatar Sep 18 '21 10:09 semlinker

type Fn = (a: number, b: string) => number
type AppendArgument<F, A> = // 你的实现代码

type FinalFn = AppendArgument<Fn, boolean> 
type AppendArgument<F extends (...args: any[]) => any, A> = (a: A, ...args: Parameters<F>) => ReturnType<F>;

winfa avatar Sep 22 '21 15:09 winfa

type Fn = (a: number, b: string) => number
// 使用infer推断   infer要配合extends使用
type AppendArgument<F extends (...args: any) => any, A> = F extends (
	...args: infer P
) => infer R
	? (x: A, ...args: P) => R
	: never

type FinalFn = AppendArgument<Fn, boolean>
// (x: boolean, a: number, b: string) => number

overQ-N avatar Sep 23 '21 13:09 overQ-N

type Fn = (a: number, b: string) => number
type AppendArgument<F extends (...args: any) => any, A> = (x: A, ...arg: Parameters<F>) => ReturnType<F>

// 测试用例
type FinalFn = AppendArgument<Fn, boolean>
// (x: boolean, a: number, b: string) => number

Mrlgm avatar Sep 23 '21 17:09 Mrlgm

type Fn = (a: number, b: string) => number
type AppendArgument<F extends (...args: any) => any, A> = (x: boolean, ...arg: F extends (...args: infer P) => any
  ? P : never) => F extends (...args: any) => infer P
  ? P : never

// 测试用例
type FinalFn = AppendArgument<Fn, boolean>
// (x: boolean, a: number, b: string) => number

Mrlgm avatar Sep 24 '21 08:09 Mrlgm

// 定义一个工具类型 AppendArgument,为已有的函数类型增加指定类型的参数,新增的参数名是 x,将作为新函数类型的第一个参数。具体的使用示例如下所示:

type Fn = (a: number, b: string) => number
type AppendArgument<F extends (...args: any[]) => any, A> = (x: A, ...args: Parameters<F>) => ReturnType<F>;

type FinalFn = AppendArgument<Fn, boolean>
// (x: boolean, a: number, b: string) => number

这个题较为简单,利用自带的 Parameters, ReturnType 分别去获取F的参数和返回值类型。 泛型 F 定义一个通用的函数类型约束即可。

zhaoxiongfei avatar Oct 01 '21 13:10 zhaoxiongfei

此答案对本题做了优化: 使用元组类型支持新增多个前置参数

/**
 * 以函数类型 F,并新增以元组类型为 T 的多个前置参数构造新类型
 */

type Fn = (a: number, b: string) => number;

// 定义非空元组约束类型
type NonEmptyTuple = [unknown, ...unknown[]];

// 1. 使用 Parameters 和 ReturnType 工具类型实现
type AppendArgument<F extends (...arg: any) => any, T extends NonEmptyTuple> = (
  ...args: [...T, ...Parameters<F>]
) => ReturnType<F>;

type FinalFn = AppendArgument<Fn, [boolean, number]>;
// (args_0: boolean, args_1: number, args_2: number, args_3: string) => number

// 2. 使用 infer 方式实现
// 非空元组类型
type AppendArgument<F, T extends NonEmptyTuple> = F extends (
  ...args: infer Args
) => infer Reture
  ? (...args: [...T, ...Args]) => Reture
  : never;

type FinalFn = AppendArgument<Fn, [boolean, number]>;
// (args_0: boolean, args_1: number, args_2: number, args_3: string) => number

GO-DIE avatar Oct 15 '21 09:10 GO-DIE

type Fn = (a: number, b: string) => number;

type AppendArgument<F extends (...args: any) => any, A> = ( x: A, ...args: Parameters<F> ) => ReturnType<F>;

type AppendArgument2<F, T> = F extends (...args: infer Args) => infer Return ? (x: T, ...args: Args) => Return : never;

type FinalFn = AppendArgument<Fn, boolean>; type FinalFn2 = AppendArgument2<Fn, boolean>;

yanglinxiao avatar Nov 18 '21 03:11 yanglinxiao

type Fn = (a: number, b: string) => number type AppendArgument<F, A> = F extends (...args: infer C) => infer B ? ( (x: A, ...args: C) => B ) : never

type FinalFn = AppendArgument<Fn, boolean> // (x: boolean, a: number, b: string) => number

a572251465 avatar Dec 01 '21 05:12 a572251465

type AppendArgument<F extends Function, A> = F extends (...args: infer G) => infer R ? (x: A, ...arg: G) => R : never
type Fn = (a: number, b: string) => number
type FinalFn = AppendArgument<Fn, boolean>

ShawDuChen avatar Dec 28 '21 10:12 ShawDuChen

type Fn = (a: number, b: string) => number // 泛型T被约束为是一个函数类型 args:Parameters 用来提取函数的参数 返回一个元祖 ReturnType用来获取函数的返回值类型 type AppendArgument<F extends (.....args:any) =>any, A> = (x:A,...args:Parameters<F>)=>ReturnType(F)

type FinalFn = AppendArgument<Fn, boolean>

kzk9527 avatar Jan 26 '22 02:01 kzk9527

解题思路

以终为始,这个工具最终要返回一个新函数类型,这个新类型只比老类型多了一个参数;

因此,就必须确保其他参数原封不动传给新函数;

这就可以用到infer关键字;

infer可以在extends的条件语句中,定义一个变量,供其他地方使用。

直接看代码吧,不难:

type AppendArgument<F, T> = F extends (...args: infer Args) => infer Return ?
    (x: T, ...args: Args) => Return : never

junbin123 avatar Feb 28 '22 01:02 junbin123

// 字符串模式匹配 存储靠infer type Fn = (a: number, b: string) => number type AppendArgument<F, A> = F extends (...args: infer Params) => infer Ret ? (x: A, ...args: Params) => Ret : never

type FinalFn = AppendArgument<Fn, boolean> // (x: boolean, a: number, b: string) => number

waleiwalei avatar Mar 11 '22 06:03 waleiwalei


type Fn = (a: number, b: string) => number

type AppendArgument<F extends (...args: any[]) => any, addedType> = (x: addedType, ...args: Parameters<F>) => ReturnType<F>;

type FinalFn = AppendArgument<Fn, boolean> 
// (x: boolean, a: number, b: string) => number

codeyourwayup avatar Mar 27 '22 06:03 codeyourwayup

感觉这样实用点,一般是往后插一个或多个参数。

type Fn = (a: number, b: string) => number;

type AppendArgument<F extends (...args: any[]) => any, A extends unknown[]> = 
  (...args: [...Parameters<F>, ...A]) => ReturnType<F>;

type AppendFn = AppendArgument<Fn, [c: number, x: unknown]>; // (a: number, b: string, c: number, x: unknown) => number

kovarxu avatar Apr 20 '22 03:04 kovarxu

type Fn = (a: number, b: string) => number

type AppendArgument<F extends (...args:any) => any, A> = F extends (...args:infer P) => infer R ? (x:A, ...arg:P) => R : never

type FinalFn = AppendArgument<Fn, boolean> 
// (x: boolean, a: number, b: string) => number

/*
思路
1. 首先将F约束为函数类型
2. infer推导类型并保存到P,R上
3. 如果真,参数类型为(x:A, a:number, b: number) => A | number,否则为never
*/
type Fn = (a: number, b: string) => number
type AppendArgument<F extends (...args:any) => any, A> = (x:A, ...arg:Parameters<F>) => ReturnType<F>
type FinalFn = AppendArgument<Fn, boolean> 
// (x: boolean, a: number, b: string) => number

/*
思路
1. Parameters<T>用于获取函数参数类型组成的元组
2. ReturnType<T>用于获取函数的返回值类型
*/

YJCCreateAHistory avatar May 24 '22 09:05 YJCCreateAHistory

type AppendArgument<F, Argument> = F extends (a: infer A, b: infer B) => infer R ? (x: Argument, a: A, b: B) => R : never

hezhisheng1930 avatar Jul 13 '22 08:07 hezhisheng1930

type Fn = (a: number, b: string) => number;
type AppendArgument<F, A> = F extends (...args: any) => any ? (x: A, ...arg: Parameters<F>) => ReturnType<F> : never; // 你的实现代码

type FinalFn = AppendArgument<Fn, boolean>;

fishcoderman avatar Aug 25 '22 15:08 fishcoderman

type Fn = (a: number, b: string) => number;
type AppendArgument<F, A> = F extends (...args: any) => any ? (x: A, ...arg: Parameters<F>) => ReturnType<F> : never; // 你的实现代码

type FinalFn = AppendArgument<Fn, boolean>;

fishcoderman avatar Aug 25 '22 15:08 fishcoderman

type Fn = (a: number, b: string) => number

// typescript是类型体操,不是值体操,不要搞混了;泛型是类型;
// 把TS当成一门语法,只不过和强类型语法不同的是,它的类型声明在变量后面,并且它支持很复杂的类型声明、定义和别名设置
type AppendArgument1<F extends (...args: any) => any, A> = (x: A, ...args: Parameters<F>) => ReturnType<F>;
type AppendArgument2<F, A> = F extends (...args: infer Args) => infer Return ? (x: A, ...args: Args) => Return : never;

type FinalFn1 = AppendArgument1<Fn, boolean>
type FinalFn2 = AppendArgument2<Fn, string>

SmileHeart1996 avatar Sep 14 '22 16:09 SmileHeart1996

type Fn = (a: number, b: string) => number

//方法一
type AppendArgument<F extends (...args:any)=>any, A> = (
	X:A,
	...args:Parameters<F>
	//Parameters获得函数参数类型组合,解构赋值
    //ReturnType获得函数返回值类型
)=> ReturnType<F>

//方法二
//通过推断关键字标记点
type AppendArgument2<F, A> = F extends (...args:infer Args)=>infer Return?(x:A,...args:Args)=>Return:never

type FinalFn = AppendArgument<Fn, boolean> 

shiyicyh avatar Aug 21 '23 02:08 shiyicyh

type AppendArgument<F, A> = F extends (...args: infer P) => infer R ? (x: A, ...args: P) => R : F

SweeperDuan avatar Dec 11 '23 08:12 SweeperDuan