你需要知道 TypeScript 的那些事<三>

383 阅读4分钟

你需要知道 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 学习小组,共同学习。有兴趣的话,拉你入群。之后还会有多种形式的对赌学习哦!