TS高级用法总结《一》

318 阅读3分钟

TS高级用法总结《一》

本文主要内容:在 function 中使用泛型、在 class 中使用泛型和extends keyof 的使用

本文阅读时间大约为6分钟,请听笔者娓娓道来。

在 function 中使用泛型

泛型可以理解为是函数的参数

function identity(arg: number): number {
  return arg;
}

上述例子,表示传入参数和返回值都必须是 number 类型;当然也可以使用 any,使用 any 时丢失了 ts 的功效。在日常的开发项目中,建议少用 any 类型。

泛型常用的写法

function identity<Type>(arg: Type): Type {
  return arg;
}
// 上面就是 Type 就是 string 类型
let output = identity("myString");

如果此时 arg 是个数组,上面 identity 方法就需要改写

function loggingIdentity<Type>(arg: Type): Type {
  console.log(arg.length);
  // Property 'length' does not exist on type 'Type'.
  return arg;
}
// === 改写成 Type[] 推荐使用 👍👍👍 ===
function loggingIdentity<Type>(arg: Type[]): Type[] {
  console.log(arg.length);
  return arg;
}
// === 也可以 Array<Type> ===
function loggingIdentity<Type>(arg: Array<Type>): Array<Type> {
  console.log(arg.length); // Array has a .length, so no more error
  return arg;
}

函数字面量的形式定义泛型

function identity<Type>(arg: Type): Type {
  return arg;
}
let myIdentity: { <Type>(arg: Type): Type } = identity;

上述方式是通过字面量的形式定义泛型,笔者认为可阅读性不强,咱们可以稍微改造使用接口的方式。

interface GenericIdentityFn<Type> {
 (arg: Type): Type;
}
type cGenericIdentityFn = GenericIdentityFn <number>
let myIdentity: cGenericIdentityFn = (num:number) => return 12;

咱们定义了一个 GenericIdentityFn 接口,再使用类型别名 type 定义了一个 number 类型的 GenericIdentityFn,然后再实现 myIdentity。

虽然,很绕但是好处就是当项目大的时候,所有来源的类型都可知道。Vue3 源码中的 ts 比这还绕,相信看过的同学都有深刻的印象。

在 class 中使用泛型

使用示例

class GenericNumber<NumType> {
  zeroValue: NumType;
  add: (x: NumType, y: NumType) => NumType;
}
let myGenericNumber = new GenericNumber<number>();
  myGenericNumber.zeroValue = 0;
  myGenericNumber.add = function (x, y) {
  return x + y;
};
// 或者
let stringNumeric = new GenericNumber<string>();
  stringNumeric.zeroValue = "";
  stringNumeric.add = function (x, y) {
  return x + y;
};

通过泛型,一个类可以生成多种类型的实例

extends & keyof

extends

之前我们处理下面这种方式的时候,是让 arg:Type -> arg:Type[]

function loggingIdentity<Type>(arg: Type): Type {
  console.log(arg.length);
  // Property 'length' does not exist on type 'Type'.
  return arg;
}

现在我们可以通过继承的方式来妥善处理上述的问题

interface Lengthwise {
  length: number;
}
function loggingIdentity<Type extends Lengthwise>(arg: Type): Type {
  console.log(arg.length); // Now we know it has a .length property, so no more error
  return arg;
}
loggingIdentity({ length: 10, value: 3 });

keyof

可以声明受其他类型参数约束的类型参数。例如,这里我们希望从给定名称的对象获取一个属性。我们希望确保我们不会意外地抓取 obj 上不存在的属性,因此我们将在两种类型之间设置一个约束

// Key extends keyof Type 表示 第二个参数 一定是 Type 中的 key
// 并且这是返回一个联合类型 union
function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) {
  return obj[key];
}
let x = { a: 1, b: 2, c: 3, d: 4 };
getProperty(x, "a");
getProperty(x, "m");
// Argument of type '"m"' is not assignable to parameter of type '"a" | "b" | "c" | "d"'.

Keyof 操作符接受对象类型,并生成其键的字符串或数值文字联合。下列类型 p 与“x”| “y”类型相同

type Point = { x: number; y: number };
type P = keyof Point;

如果类型有字符串或数字索引签名,keyof 将返回这些类型:

type Arrayish = { [n: number]: unknown };
type A = keyof Arrayish;
// type A = number

type Mapish = { [k: string]: boolean };
type M = keyof Mapish;
// type M = string | number

注意,在这个例子中,m 是字符串 | number ー这是因为 JavaScript 对象键总是被强制为字符串,所以 obj [0]总是与 obj [“0”]相同。

总结

阅读 Vue3 源码,如果不知道 ts 的高级用法,你可能会被绕晕;如果你是初次阅读源码,建议在社区内找一个“简单”版本学习,咳咳!

👍👍👍

推荐源码阅读从 mini-vue 开始:gitHub 仓库,实现了最简的 Vue 模型!