你需要知道 TypeScript 的那些事<三>
写在前面:本次 ts 系列文章共4篇,从入门到"入土"系列,本文为第三篇,且本文仅作为学习笔记。
少年常年游荡于江湖,却不曾放下手中之笔,因其所知甚少,唯善学!
上回说到:TS 是 JS 的超集(包含关系);TS 中对于变量、函数接收参数和返回参数等都有严格的类型判断;TS 中 type 类型别名和 interface 接口 的区别,大体上没有区别,但是在扩展、继承等方面还是有些差异;详情请查看:第一篇链接;TS 中类型断言(as)和 类型收窄(in\typeof\instanceof)的用法以及文字类型和枚举类型示例;详情请查看:第二篇链接;
本文主要内容:函数相关约束、泛型、重载
本文阅读时间大约为8分钟,请听笔者娓娓道来。
函数相关约束
函数类型表达式
举个🌰
// 两个函数都限制了 s 的类型
function greeter(fn: (a: string) => void) {
// 如果传入的不是 string 就不报错
fn("Hello, World");
}
function printToConsole(s: string) {
console.log(s);
}
greeter(printToConsole);
// 当然,我们可以使用类型别名来命名函数类型:
type GreetFunction = (a: string) => void;
function greeter(fn: GreetFunction) {
// ...
}
调用签名
在 JavaScript 中,函数除了可调用之外,还可以具有属性。但是,函数类型表达式语法不允许声明属性。如果我们想用属性来描述可调用的东西,我们可以用对象类型来写一个调用签名:
举个🌰
type DescribableFunction = {
description: string;
(someArg: number): boolean;
};
function doSomething(fn: DescribableFunction) {
console.log(fn.description + " returned " + fn(6));
}
构造签名
函数也可以用新的操作符来调用。引用这些作为构造函数,因为它们通常会创建一个新对象。你可以通过在调用签名前面添加 new 关键字来写一个构造签名:
举个🌰
type SomeConstructor = {
new (s: string): SomeObject;
};
function fn(ctor: SomeConstructor) {
return new ctor("hello");
}
泛型
泛型:[generic - 通用、泛指的意思],那最简单的理解,泛型就是泛指的类型。泛型的定义使用<>(尖角号)进行定义的;
举个🌰
// 定义一个联合类型
function join(first: string | number, second: string | number) {
return `${first}${second}`;
}
join("juejin", ".cn");
// 这个方法现在没有任何问题,但现在有这样一个需求,就是 first 参数如果传的是字符串类型,要求 second 也传字符串类型。同理,如果是 number 类型,就都是 number 类型
function join<T>(first: T, second: T) {
return `${first}${second}`;
}
// 限制了二者的类型 必须相同
join<string>("juejin", ".cn");
join<number>(1, 2);
T 可以看成一种特殊的标识符,传进来是什么,它就变成什么。
泛型数组
如果传递过来的值要求是数字,如何用泛型进行定义两种方法,第一种是直接使用[],第二种是使用Array<泛型>。形式不一样,其他的都一样。
举个🌰
function myFun<T>(params: T[]) {
return params;
}
myFun<string>["123", "456"];
function myFun<T>(params: Array<T>) {
return params;
}
myFun<string> ["123", "456"];
多个泛型定义
举个🌰
function join<T, P>(first: T, second: P) {
return `${first}${second}`;
}
join <number,string> (1, "2");
泛型的使用方法
举个🌰
class SelectHero<T> {
constructor(private hero: T[]) {}
getHero(index: number): T {
return this.hero[index];
}
}
const selectHero = new SelectHero(["法外狂徒", "疾风剑豪", "虚空之女"]);
console.log(selectHero.getHero(1));
泛型中的继承
举个🌰
interface Hero {
name: string;
}
class SelectHero<T extends Girl> {
constructor(private hero: T[]) {}
getHero(index: number): T {
return this.hero[index].name;
}
}
const selectHero = new SelectHero([{name:"法外狂徒"},{name:"疾风剑豪"},{name:"虚空之女"}]);
console.log(selectHero.getHero(1));
函数重载
重载:函数名相同,但是参数个数或类型不同
一些 JavaScript 函数可以通过各种参数计数和类型来调用。例如,您可以编写一个函数来生成一个接受时间戳(一个参数)或月/日/年规范(三个参数)的 Date。
举个🌰
function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
// 两个可选 并不意味着 可以传 2 个参数 我们需要传入 1 或 3 个参数
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
if (d !== undefined && y !== undefined) {
return new Date(y, mOrTimestamp, d);
} else {
return new Date(mOrTimestamp);
}
}
const d1 = makeDate(12345678);
const d2 = makeDate(5, 5, 5);
const d3 = makeDate(1, 3);
// No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.
总结
路漫漫其修远兮,吾将上下而求索。本文主要讲述了函数相关约束、泛型和重载一些用法。其中,泛型是重点,一定要好好把握!如有不当之处,请不吝赐教。最后,笔者近期加入了 mini-vue 作者创建的 ts 学习小组,共同学习。有兴趣的话,拉你入群。之后还会有多种形式的对赌学习哦!