AnnGreen1 / article

0 stars 0 forks source link

TS 泛型你还不会?来!我教你 #16

Open AnnGreen1 opened 3 months ago

AnnGreen1 commented 3 months ago

前言 :最近遇到了一些写作上的烦恼,自己好像陷入了程序员的通病想法,"这个知识点这么简单,大家应该都会吧,我说出来是不是显得我很笨。"

思考了近一个月,又翻了翻自己最开始写作的文章,文笔虽显稚嫩,但初心是真真正正想和大家分享某一个知识点。那时候的写作不为求阅读量,不为凸显自己的水平,只求能把语言尽可能用大白话表达出来,帮助同样是初学者的某位读者。

而现在怎么随着工作时间的增长,反而越来越违背自己写作的初心了呢?于是就诞生了这篇文章的写作契机。

注意:本文面向 TS 泛型初学者,遂只会用通俗的口语来表达。不会引经据典插入官网文档,我要做的就是让你初步理解它解决了什么事情。

一. 场景再现

  1. 我们先不要去想泛型这两个字是什么意思,我们先思考下面这个场景。

  2. 有这样一个需求,这个函数可以传递一个字符串 ,并且把参数原封不动返回。你心想,太简单了,一秒钟实现,ok 工资到手。
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp1-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F19ffda22ad9c4c1588f62ba3adf655c6~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D335%26h%3D69%26s%3D9614%26e%3Dpng%26b%3D222435&valid=false)

  3. 产品经理这时候又跑来加了个需求,要求也能输入数字类型 ,ok,没问题,简单的。
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp3-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F348e0032830a43469a4d0e70e0bf0ba5~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D395%26h%3D69%26s%3D10686%26e%3Dpng%26b%3D222435&valid=false)

  4. 没一会,产品经理又说:不行不行,布尔值 也得加上。你虽然抱怨了几句,但还是很快的修改完成了修改。
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp9-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F1775f5d1149c48debfa9b77dff1ce9ec~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D468%26h%3D67%26s%3D11844%26e%3Dpng%26b%3D222435&valid=false)

  5. "emm,那个韩同学呀。我想了一下,还得加上 undefined ,和 null "这样才完整,看着产品小姐姐满脸笑容的样子,你也生气不起来,于是又加上了两行代码。
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp3-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F3cc313a68d784afd8c466c2c3e619687~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D626%26h%3D73%26s%3D13566%26e%3Dpng%26b%3D222435&valid=false)

  6. 现在功能倒是实现了,但是你看着这个函数陷入了沉思,倒不是因为产品经理的需求让你烦恼,而是有一丢丢代码洁癖的你开始思考有没有什么方法让这么一大串的东西简单点呢?🤔

二. 理清线索

  1. 我们将刚刚的上面这段代码搬到下面来。

    function say(txt: string | number | boolean | undefined | null) {
        return txt;
    }
  2. 先别着急,我们捋一下目前我们已掌握的线索🤔:(1)这个函数会接受一个参数 。(2)这个函数有一个返回值 。(3)这个函数的返回值的类型参数的类型是相对应的。

  3. 你突然灵光一闪,我怎么给忘了一个神器了呢?any 救我,好家伙,那你和直接写 js 有什么区别呢?
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp1-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F6ff75b8220124646b892df95184045cc~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D292%26h%3D73%26s%3D9170%26e%3Dpng%26b%3D222435&valid=false)

  4. 那到底有没有什么更加简单的方法就能达成我们最初的需求呢?你别说,还真有,加下来引入今天的主角泛型

三. 泛型的用途

  1. 不要被这个名字给吓唬到了,刚开始我不敢去理解这个知识点,有百分之90的原因就是因为泛型 这个名字就给人一种很高冷的感觉,但其实理解起来十分简单,只不过它的写法有些特殊,仅此而已。接下来我不会提及"泛型"二字,只让你理解它是干什么的。

  2. 这是我们上面的代码,TS 老给我划红线提示我没传参数,我也很急啊,因为我真的不知道该写什么类型,我现在也没确定。
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp9-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F0ab4bd0f5e2d423b9d3fbdc2815f87e3~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D293%26h%3D110%26s%3D8576%26e%3Dpng%26b%3D222435&valid=false)

  3. 这咋办,于是你在想,我能不能先随便写个东西占着地方,等我真正用的时候再告诉你我需要什么类型。

  4. 于是你随手写了一堆符号,先占住位置,但是你并没有声明这个 XXXXX 的类型啊,TS 不知道啊,那我肯定不给你通过的。
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp1-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2Fc1c17bd7fae2481ea4d6905512c41b5f~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D286%26h%3D114%26s%3D9886%26e%3Dpng%26b%3D222435&valid=false)

  5. 这咋整,这不让,那不让,那我还写不写代码了?别急嘛,人家 TS 也不是那么无情,慷慨的甩给你两个尖括号<>,也就是键盘上的大于号\&小于号 。你和 TS 各退一步,TS 允许你在图书馆占座,但是你得告诉它占座的人是谁,占座的那个人你得用<>包起来。

  6. 在哪占座?TS 环顾四周,指着这个位置,"那你就在这坐吧。"
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp9-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F890b8822df2149d5a8f067145854f773~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D697%26h%3D275%26s%3D21034%26e%3Djpg%26b%3D222335&valid=false)

  7. 怎么占座?人家不告诉你了嘛?用<>包起来。
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp9-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F2bbd7b2f7fbc43b79d2292f5804c91a2~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D372%26h%3D119%26s%3D10642%26e%3Dpng%26b%3D222435&valid=false)

  8. 此时我们再看 TS 的类型提示,返回值参数类型 都变为了占座的这个人。
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp1-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F07f41688b82d473dad2d0696c3ff0e02~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D697%26h%3D158%26s%3D73658%26e%3Dpng%26b%3D202233&valid=false)

  9. 当我们实际调用的时候,让占座的人走开,把已知的参数放进去即可。
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp1-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F13d1fe75acc14f4c8aa727dd0b8201a7~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D697%26h%3D252%26s%3D117219%26e%3Dpng%26b%3D222435&valid=false)
    此时我们再观察函数提示,就会发现 TS 自动帮我们替换掉了 XXXXX 占位符。
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp1-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2Fb6d5c2b04ae34a31bd3f28b300b417fe~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D697%26h%3D153%26s%3D48166%26e%3Dpng%26b%3D232537&valid=false)

  10. 到这里你其实会发现,我并没有用常规的大写 T 来这里占位,而是用来一个很不规则的 xxxxx 来占位,其实就是想告诉大家,官方文档用 T ,并不代表它只能用 T不要把自己局限在"别人这样写,我就只能这样写。" 否则当别人问及你的时候,你只能回答:"我也不知道,我看别人都这样写,所以我才这样写。 " 这样其实很不好。
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp3-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2Fa3e046c8515e419eb7c0b6bb53123865~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D808%26h%3D382%26s%3D67098%26e%3Dpng%26b%3Dfbfbfb&valid=false)
    tips :官方这里用 T 其实是取字母 type 的首字母来表达类型占位 的概念,写 T 更多的是代码规范而不是定死的结论。

四. 你其实正在使用泛型

  1. vue3 的小伙伴其实对这种写法已经熟悉的不能再熟悉了。
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp3-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F30815aa8eef14d1dbb8ea0f557f54c72~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D386%26h%3D108%26s%3D14311%26e%3Dpng%26b%3D222435&valid=false)

  2. 你也早已习惯 vue 的类型提示,因为我 namestring 类型,所以自然而然有下面这些提示。
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp9-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F9b2dbf74ae4c4d96a70a7603efab7333~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D697%26h%3D436%26s%3D109867%26e%3Dpng%26b%3D1f2133&valid=false)

  3. 但不要忘了,vue 只是包装了 js,它本身并不提供类型提示,这时你可能开始好奇了,这是怎么回事?🤔

  4. 不要忘了这个 ref 其实是一个函数!!!
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp9-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F3a8d2a97f350452582d236be93f23012~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D697%26h%3D190%26s%3D25346%26e%3Djpg%26b%3D222436&valid=false)

  5. 你上面的写法其实等价于下面的写法。
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp6-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2Fa5e0c070a383405cbf2e4f20b94857bf~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D697%26h%3D196%26s%3D71500%26e%3Dpng%26b%3D232436&valid=false)

  6. 眼熟吗?这不就是我们刚刚提到的占位 吗?
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp1-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F9cb4ec0de4834f47b42c301f6074dada~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D372%26h%3D119%26s%3D11382%26e%3Dpng%26b%3D222435&valid=false)
    vue 其实不就是这样定义 ref 函数的吗?
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp3-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2Fdcaf0f10e8d74ea086f5e13a6ba56efa~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D308%26h%3D106%26s%3D9594%26e%3Dpng%26b%3D222435&valid=false)

  7. computed 也是同样的道理。
    ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp1-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2Ffae0852de6424c15a1b1badac1dd6c71~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D426%26h%3D107%26s%3D17449%26e%3Dpng%26b%3D222435&valid=false)

  8. 只不过我们开发时省略了占位类型 ,因为 TS 会通过函数的返回值类型帮你做这一步。

五. 补充

  1. 泛型其实和函数的形参的概念很类似,你可以对比这两个概念的实际用法。

  2. 泛型不止可以传递一个,可以传递多个。 ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp3-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F92e8a582b6b6477b918726799056204a~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D441%26h%3D33%26s%3D8986%26e%3Dpng%26b%3D222435&valid=false)

  3. 泛型可以有自己的默认类型,写法如下。 ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp3-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F5861292a081e40b58e6e40f361b94ab3~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D697%26h%3D134%26s%3D40813%26e%3Dpng%26b%3D222335&valid=false)

  4. 泛型还有更加灵活的用法,比如约束类型中的某个类型。 ![](https://cubox.pro/c/filters:no_upscale()?imageUrl=https%3A%2F%2Fp1-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2Feaf5a3102fea49d399a4f1dbff2a1f8c~tplv-k3u1fbpfcp-jj-mark%3A3024%3A0%3A0%3A0%3Aq75.awebp%23%3Fw%3D349%26h%3D214%26s%3D20516%26e%3Dpng%26b%3D222435&valid=false)

  5. 泛型还有更多灵活的组合方法,需要读者遇到具体的场景时,通过这些前置知识组合出自己的泛型约束,届时会体会的更加深刻,本文不做过多探讨。

作者:韩振方 链接:https://juejin.cn/post/7281560175676375080 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。