【Typescript入门手册】函数,详细描述关于面试的知识点

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Web前端全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

let mySum2: (x: number, y: number) => number = function(

x: number,

y: number

): number {

return x + y;

};

注意:此箭头,并不是 ES6 中我们熟知的箭头函数哦~

两者的区别是 mySum1 的类型是推导出来的,而 mySum2 是直接定义的,但两者其实一样,不需要深究。

在这里插入图片描述

二、函数签名


在 JavaScript 中,函数除了可以被调用,自己也是可以有属性值的。然而上一节讲到的函数类型表达式并不能支持声明属性,如果我们想描述一个带有属性的函数,我们可以在一个对象类型中写一个调用签名(call signature)。

type DescribableFunction = {

description: string;

(someArg: number): boolean; // 参数列表和返回值类型

};

function doSomething(fn: DescribableFunction) {

console.log(fn.description + " returned " + fn(6));

}

// 测试func

const func = function(someArg: number): boolean {

return someArg > 5;

};

func.description = “携带的描述”;

doSomething(func);

三、泛型函数


  1. 指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。记住
  1. 所谓泛型就是用一个相同类型来关联两个或者更多的值。

我们经常需要写这种函数,即函数的输出类型依赖函数的输入类型,或者两个输入的类型以某种形式相互关联。让我们考虑这样一个函数,它返回数组的第一个元素:

function firstElement(arr: any[]) {

return arr[0];

}

注意此时函数返回值的类型是 any,如果能返回第一个元素的具体类型就更好了。

此时我们需要在函数签名里声明一个类型参数 (type parameter):

function firstElement(arr: T[]): T {

return arr[0];

}

const str = firstElement([‘str’]);

// const str: string

const bool = firstElement([true]);

// const bool: boolean

const num = firstElement([1]);

// const num: number

通过给函数添加一个类型参数 T,并且在两个地方使用它,我们就在函数的输入(即数组)和函数的输出(即返回值)之间创建了一个关联。现在当我们调用它,一个更具体的类型就会被判断出来;

3.1 推断(Inference)

注意在上面的例子中,我们没有明确指定T的类型,类型是被 TypeScript 自动推断出来的。

我们也可以使用多个类型参数,举个例子:

const res = map([1, 2, 3, 4], (val) => val.split(“,”));

// (parameter) val: number

// 类型“number”上不存在属性“split”。

注意在这个例子中,TypeScript 既可以推断出 Input 的类型是number ,此时自然不能使用split方法。

3.2 约束(Constraints)

有的时候,我们想关联两个值,但只能操作值的一些固定字段,这种情况,我们可以使用**约束(constraint)**对类型参数进行限制。

让我们写一个函数,函数返回两个值中更长的那个。为此,我们需要保证传入的值有一个number类型的length属性。我们使用extends语法来约束函数参数:

function longest<Type extends { length: number }>(a: Type, b: Type) {

if (a.length >= b.length) {

return a;

} else {

return b;

}

}

const res1 = longest([1, 2, 3], 1);

// ❌ 类型“number”的参数不能赋给类型“{ length: number; }”的参数。

const res2 = longest([1, “2”, 3], [1]);

// const res2: (string | number)[]

const res3 = longest(‘yuguang’, ‘余光’);

// const res3: “yuguang” | “余光”

正是因为我们对Type做了{ length: number }限制,我们才可以被允许获取 a b参数的 .length 属性。没有这个类型约束,我们甚至不能获取这些属性,因为这些值也许是其他类型,并没有 length 属性。

基于传入的参数,longerArray和 longerString 中的类型都被推断出来了。

3.3 泛型约束实战(Working with Constrained Values)

这是一个使用泛型约束常出现的错误:

function minimumLength<Type extends { length: number }>(

obj: Type,

minimum: number

): Type {

if (obj.length >= minimum) {

return obj;

} else {

return { length: minimum };

// 不能将类型“{ length: number; }”分配给类型“Type”。

// “{ length: number; }” 可赋给 “Type” 类型的约束

// 但可以使用约束 “{ length: number; }” 的其他子类型实例化 “Type”。

}

}

这里很绕,我也理解了很久,欢迎大家讨论交流哦~

其中的问题就在于函数理应返回与传入参数相同类型的对象,而不仅仅是符合约束的对象。我们可以写出这样一段反例:

3.4 声明类型参数 (Specifying Type Arguments)

TypeScript 通常能自动推断泛型调用中传入的类型参数,但也并不能总是推断出。举个例子,有这样一个合并两个数组的函数:

function combine(arr1: Type[], arr2: Type[]): Type[] {

return arr1.concat(arr2);

}

// 如果你像下面这样调用函数就会出现错误:

const arr1 = combine([1, 2, 3], [“hello”]);

// 不能将类型“string”分配给类型“number”

// 而如果你执意要这样做,你可以手动指定 Type:

const arr2 = combine<string | number>([1, 2, 3], [“hello”]);

四、写一个好的泛型函数的一些建议


尽管写泛型函数很有意思,但也容易翻车。如果你使用了太多的类型参数,或者使用了一些并不需要的约束,都可能会导致不正确的类型推断。

4.1 类型参数下移(Push Type Parameters Down)

下面这两个函数的写法很相似:

function firstElement1(arr: Type[]) {

return arr[0];

}

function firstElement2<Type extends any[]>(arr: Type) {

return arr[0];

}

// a: number (good)

const a = firstElement1([1, 2, 3]);

// b: any (bad)

const b = firstElement2([1, 2, 3]);

分析:

  • 第一个函数可以推断出返回的类型是 number,但第二个函数推断的返回类型却是 any,因为 TypeScript 不得不用约束的类型来推断 arr[0] 表达式,而不是等到函数调用的时候再去推断这个元素。

4.2 使用更少的类型参数 (Use Fewer Type Parameters)

这是另一对看起来很相似的函数:

// good

function filter1(arr: Type[], func: (arg: Type) => boolean): Type[] {

return arr.filter(func);

}

// bad

function filter2<Type, Func extends (arg: Type) => boolean>(

arr: Type[],

func: Func

): Type[] {

return arr.filter(func);

}

我们创建了一个并没有关联两个值的类型参数 Func,这是一个危险信号,因为它意味着调用者不得不毫无理由的手动指定一个额外的类型参数。Func 什么也没做,却导致函数更难阅读和推断。

4.3 类型参数应该出现两次 (Type Parameters Should Appear Twice)

有的时候我们会忘记一个函数其实并不需要泛型

// bad

function greet(s: Str) {

console.log("Hello, " + s);

}

// good

function greet(s: string) {

console.log("Hello, " + s);

}

记住:类型参数是用来关联多个值之间的类型。如果一个类型参数只在函数签名里出现了一次,那它就没有跟任何东西产生关联。如果一个类型参数仅仅出现在一个地方,强烈建议你重新考虑是否真的需要它。

在这里插入图片描述

五、函数参数


  1. 可选参数

  2. 参数默认值

  3. 剩余参数

  4. 参数解构

5.1 可选参数

如果你已经对函数的参数类型声明,那么输入多余的(或者少于要求的)参数,是不允许的。那么如何定义可选的参数呢?

与接口中的可选属性类似,我们用?表示可选的参数:

function hello(name1: string, name2: string): void {

console.log(hello! ${name1} ${name2 ? "and" + " " + name2 : ""});

}

hello(“余光”);

// 应有 2 个参数,但获得 1 个

// 未提供 “name2” 的自变量。

function hello1(name1: string, name2?: string): void {

console.log(hello! ${name1} ${name2 ? "and" + " " + name2 : ""});

}

hello1(“余光”);

需要注意的是,可选参数必须接在必需参数后面。换句话说,可选参数后面不允许再出现必需参数了:

5.2 参数默认值

在 ES6 中,我们允许给函数的参数添加默认值,TypeScript 会将添加了默认值的参数识别为可选参数:

function hello(name1: string = “余光”, name2: string = “yuguang”): void {

console.log(hello! ${name1} ${name2 ? "and" + " " + name2 : ""});

}

hello(); // hello! 余光 and yuguang

hello(“小明”); // hello! 小明 and yuguang

是不是又拉回到了我们熟悉的领域?

5.3 剩余参数

ES6 中,可以使用 …rest 的方式获取函数中的剩余参数(rest 参数):

function func(a, …arg) {

console.log(a);

console.log(arg);

}

func(1, 2, 3, 4); // 1, [2, 3, 4]

**注意:**rest参数只能是最后一个参数

5.4 参数解构

你可以使用参数解构方便的将作为参数提供的对象解构为函数体内一个或者多个局部变量,在 JavaScript 中,是这样的:

type Man = { name: string; age: number };

function sum({ name, age }: Man) {

console.log(name + age);

}

sum({ a: 10, b: 3, c: 9 });

后话

对于面试,说几句个人观点。

面试,说到底是一种考试。正如我们一直批判应试教育脱离教育的本质,为了面试学习技术也脱离了技术的初心。但考试对于人才选拔的有效性是毋庸置疑的,几千年来一直如此。除非你有实力向公司证明你足够优秀,否则,还是得乖乖准备面试。这也并不妨碍你在通过面试之后按自己的方式学习。
其实在面试准备阶段,个人的收获是很大的,我也认为这是一种不错的学习方式。首先,面试问题大部分基础而且深入,这些是平时工作的基础。就好像我们之前一直不明白学习语文的意义,但它的意义就在每天的谈话间。

所谓面试造火箭,工作拧螺丝。面试往往有更高的要求,也迫使我们更专心更深入地去学习一些知识,也何尝不是一种好事。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
9 });

后话

对于面试,说几句个人观点。

面试,说到底是一种考试。正如我们一直批判应试教育脱离教育的本质,为了面试学习技术也脱离了技术的初心。但考试对于人才选拔的有效性是毋庸置疑的,几千年来一直如此。除非你有实力向公司证明你足够优秀,否则,还是得乖乖准备面试。这也并不妨碍你在通过面试之后按自己的方式学习。
其实在面试准备阶段,个人的收获是很大的,我也认为这是一种不错的学习方式。首先,面试问题大部分基础而且深入,这些是平时工作的基础。就好像我们之前一直不明白学习语文的意义,但它的意义就在每天的谈话间。

所谓面试造火箭,工作拧螺丝。面试往往有更高的要求,也迫使我们更专心更深入地去学习一些知识,也何尝不是一种好事。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-vnuEJnnr-1713214457933)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值