关于javascript:TypeScript中那几个奇怪的知识点

3次阅读

共计 3563 个字符,预计需要花费 9 分钟才能阅读完成。

写在结尾

  • 我写了一年多TypeScript, 总结了以下几个点,心愿能够帮到大家
  • 如果感觉写得不错,记得来个 关注 / 在看

比拟容易遇到的问题

给一个对象增加属性
interface Obj {a: string;}

const obj: Obj = {a: "1",};

obj.b = 2;
  • 此时会呈现谬误提醒:类型“Obj”上不存在属性“b”。
  • 要想解决这个问题,要应用索引签名
interface Obj {
  a: string;
  [index: string]: string | number;
}

const obj: Obj = {a: "1",};

obj.b = 2;
  • 大家很好奇,为什么我这里会退出[index: string]: string | number;, 类型是字符串或者数字。因为:

当你申明一个索引签名时,所有明确的成员都必须合乎索引签名

函数重载
  • 场景:函数有多个参数,而且参数不确定时,函数运行逻辑不统一
// 重载
function padding(all: number);
function padding(topAndBottom: number, leftAndRight: number);
function padding(top: number, right: number, bottom: number, left: number);
// Actual implementation that is a true representation of all the cases the function body needs to handle
function padding(a: number, b?: number, c?: number, d?: number) {if (b === undefined && c === undefined && d === undefined) {b = c = d = a;} else if (c === undefined && d === undefined) {
    c = a;
    d = b;
  }
  return {
    top: a,
    right: b,
    bottom: c,
    left: d
  };
}

这样函数兼容 传 1、2、4 个参数。然而只有传三个,就会报错。

  • 函数重载最重要的是,最终申明(从函数外部看到的真正申明)与所有重载兼容(与下面的索引签名统一)
下载的第三方 npm 库没有 ts 申明文件
  • 例如:
npm i somePackage --save 
import somePackage from 'somePackage';
  • 然而此时提醒:找不到模块“somePackage”或其相应的类型申明。
  • 此时你能够在我的项目根目录下新建 index.d.ts,编写如下代码:
declare module 'somePackage';
...

这个问题迎刃而解

泛型
  • 这个问题很容易困扰小白,其实泛型简略来说,就是一个 类型变量, 如下所示:
class Peter {niubi<T>(a: T): T[] {return [a];
  }
}

此时的 T 就是一个泛型, 它是一个可变的类型。依据你传入 niubi 这个办法的参数对象来确定的,当咱们传入的 a 是字符串,那么 T 就为 string. 返回的就是一个item 为字符串的数组

class Peter {niubi<T>(a: T): T[] {return [a];
  }
}

const obj = new Peter();

let res = obj.niubi("hehe");

res = 1;

res = ["2"];

此时 res = 1 会报错 不能将类型“number”调配给类型“string[]”, 因为此时 TS 推断进去,res 必然为一个数组,且外面的 item 是一个字符串.

res = ["2"]则不会报错

  • 泛型能够说是 TS 外面的一个难点,然而其实它只是一个可变的类型变量。
  • 调整参数后:
let res2 = obj.niubi(2);

res2 = 2;
  • 会报错:不能将类型“number”调配给类型“number[]”。

最初要记住的是,既然是类型变量。那么这个变量也能够是一个泛型。

class Peter {niubi<T>(a: T): T[] {return [a];
  }
}

const obj = new Peter();

function test<T>(b: T): T {return b;}

let res = obj.niubi(test(1));
  • 看到这里必定有人会说,Peter 你脱裤子放屁啊。这个还不如用 any. 那你再看上面这段代码,咱们封装 api 申请的时候。
  • 首先定义好返回的接口。(返回的接口个别都是有对立的格局,状态码和 result,data 等)
// 申请接口数据
export interface ResponseData<T = any> {
  /**
   * 状态码
   * @type {number}
   */
  code: number;

  /**
   * 数据
   * @type {T}
   */
  result: T;

  /**
   * 音讯
   * @type {string}
   */
  message: string;
}
  • 这里的 data 数据是动静的格局,咱们能够用泛型来定义。
  • 这里用了两次泛型, 先定义好返回的 data 数据,再用泛型形式传入,组装好返回的整个返回数据接口(蕴含code,result,data)。再接着传入到真正的申请函数中
// 在 axios.ts 文件中对 axios 进行了解决,例如增加通用配置、拦截器等
import Ax from './axios';

import {ResponseData} from './interface.ts';

export function getUser<T>() {return Ax.get<ResponseData<T>>('/somepath')
    .then(res => res.data)
    .catch(err => console.error(err));
}
  • 在真正的申请函数中应用了泛型,即传入任意类型参数 <T>,那么便返回一个Promise 格调的 Promise<T> 数据:
const get = <T>(config: { url: string; headers?: { [key: string]: string } }): Promise<T> => {
  const fetchConfig = {
    method: 'GET',
    Accept: 'application/json',
    'Content-Type': 'application/json',
    ...(config.headers || {})
  };
  return fetch(config.url, fetchConfig).then<T>(response => response.json());
};

总结两次泛型的间断应用:

1. 应用 data 作为泛型,传入

2. 组装成 {code,result,data} 这种类型接口

3. 将第二步的组装后类型作为泛型 <T> 传入 get 办法中

4. 返回一个 Promise 格调的 Promise<T> 数据

这样做的意义,提取可变的数据类型 data, 让 TS 推断出这个接口返回的数据是怎么样的。缩小不必要的反复代码,即每次接口调取都会返回的数据格式类型:coderesult

  • 置信你通过这段代码和文字,能真正了解 TS 的泛型如何用,什么中央应用,以及应用的意义了。
颗粒度定义类型后的问题
  • 当咱们颗粒度比拟细定义了接口当前,可能存在接口复用的问题, 例如:
interface test1 {a: string;}

interface test2 {b: string;}
  • 此时我想要定义一个两个属性都领有的对象,那么能够应用联结类型。
const obj: test1 & test2 = {
  a: "1",
  b: "2",
};
  • 如果我想定义一个只有 a/b 的对象, 能够应用
const obj: test1 | test2 = {a: "1",};

可能有人会说,怎么会写这么简略的货色。

  • 这里是为了接下来的类型兼容性打基础,TS 外面最重要的就是type 类型,类型零碎就是它的外围。
  • 咱们能够用两个不同的变量来相互赋值来测验,他们的类型是否兼容,例如:
interface Test1 {
  a: number;
  b: number;
  c: string;
}

interface Test2 {
  a: number;
  b: number;
}

let test1: Test1 = {
  a: 1,
  b: 2,
  c: "3",
};

let test2: Test2 = {
  a: 1,
  b: 2,
};

test1 = test2;

此时提醒 类型 "Test2" 中短少属性 "c",但类型 "Test1" 中须要该属性。

  • 然而当咱们用 test1 赋值给 test2 的时候:
test2 = test1;

这个时候是能够的

  • 这里其实暗藏着一些逻辑,Test1接口比 Test2 接口多一个 c 属性,Test2接口能够说是 Test1 接口的子类。这是多态性

对于如何解决、判断 TS 的类型兼容性,大家能够看上面这些类型

  • 协变(Covariant):只在同一个方向;
  • 逆变(Contravariant):只在相同的方向;
  • 双向协变(Bivariant):包含同一个方向和不同方向;
  • 不变(Invariant):如果类型不完全相同,则它们是不兼容的。

写在最初

正文完
 0