分类
typescript 源码解析

从vue3源码开始学(1)

第一次写,总感觉这没准备好,那没准备好,哪有都准备好了的,干就完事

准备工作

克隆项目 git clone https://github.com/lxchuan12/vue-next-analysis.git

计划了解的

主要要啃的是 packages\shared\src 下的函数

一个个来看下

正餐

is的用法

export const isArray: (arg: any) => arg is any[] = Array.isArray
// 首先这是TS的语法 等价于
export const isArray = Array.isArray

这种写法与 : 
export const isArray: (arg: any) => boolean = Array.isArray

有什么区别吗,看下面这两个例子

function isString(test: any): test is string{
    return typeof test === "string";
}

function example(foo: any){
    if(isString(foo)){
        console.log("it is a string" + foo);
        console.log(foo.length); // string function
        // 如下代码编译时会出错,运行时也会出错,因为 foo 是 string 不存在toExponential方法
        console.log(foo.toExponential(2));
    }
    // 编译不会出错,但是运行时出错
    console.log(foo.toExponential(2));
}
example("hello world");

// --------------------------------
function isString(test: any): boolean{
    return typeof test === "string";
}

function example(foo: any){
    if(isString(foo)){
        console.log("it is a string" + foo);
        console.log(foo.length); // string function
        // foo 为 any,编译正常。但是运行时会出错,因为 foo 是 string 不存在toExponential方法
        console.log(foo.toExponential(2));
    }
}
example("hello world");

在使用类型保护时,TS 会进一步缩小变量的类型。
例子中,将类型从 any 缩小至了 string
类型保护的作用域仅仅在 if 后的块级作用域中生效

keyof

const hasOwnProperty = Object.prototype.hasOwnProperty
export const hasOwn = (
  val: object,
  key: string | symbol
): key is keyof typeof val => hasOwnProperty.call(val, key)

这里没学过ts的同学可能有点晕,这个is刚刚上面讲过了是做类型保护的,那这个 keyof typeof 又是啥 这个val又是啥

别急,跟我一个个来

首先先看这个 keyof

keyof 操作符是在 TypeScript 2.1 版本引入的,
该操作符可以用于获取某种类型的所有键,其返回类型是联合类型。
interface Person {
  name: string;
  age: number;
  location: string;
}
type K1 = keyof Person; // "name" | "age" | "location"
type K2 = keyof Person[];  // number | "length" | "push" | "concat" | ...
type K3 = keyof { [x: string]: Person };  // string | number

除了接口外,keyof 也可以用于操作类
class Person {
  name: string = "Semlinker";
}

let sname: keyof Person;
sname = "name";

keyof 操作符除了支持接口和类之外,它也支持基本数据类型
let K1: keyof boolean; // let K1: "valueOf"
let K2: keyof number; // let K2: "toString" | "toFixed" | "toExponential" | ...
let K3: keyof symbol; // let K1: "valueOf"

此外 keyof 也称为输入索引类型查询,与之相对应的是索引访问类型,也称为查找类型。
在语法上,它们看起来像属性或元素访问,但最终会被转换为类型:
type P1 = Person["name"];  // string
type P2 = Person["name" | "age"];  // string | number
type P3 = string["charAt"];  // (pos: number) => string
type P4 = string[]["push"];  // (...items: string[]) => number
type P5 = string[][0];  // string

所以简单的抓住重点返回的是key

举个简单的例子
// js
function getProperty(obj, key) {
  return obj[key];
}

// 这个转成ts怎么写尼
function getProperty(obj: object, key: string) {
  return (obj as any)[key];
}

// 不太优雅,用上keyof
function getProperty<T extends object, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

function getProperty<我是对象 extends object, 我是对象中的键 extends keyof 我是对象>(obj: 我是对象, key: 我是对象中的键) {
  return obj[key];
}

这时候聪明的同学就要问了 老师,这个extends你还没讲呀

这个 extends 要和 keyof 连在一起看,至少按我的理解是的

keyof T 是 T 类型的键集

extends是子集的意思

子类型的值一定也是父类型。比如“猫”是“动物”的子类型,一只具体的 “猫”一定也是“动物”

所以父类型是 ‘a’|’b’ 子类型只能是 <= 父类型的

extends keyof 和 in keyof 要区分看

function getProperty<T extends object, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}
// 这里的意思是K必须是T的公共属性名称,与class的那个extends 意思不太一样甚至相反

interface Person {
  age: number;
  name: string;
}

// 每一项映射成了可选的
type Optional<T> = { 
  [K in keyof T]?: T[K] 
};

const person: Optional<Person> = {
  name: "Tobias"
};

// 看上去这个 Optional<Person> 和 Person 一模一样是吧
// 我这边的理解是 这是一种映射 从必填都变成了可选


type ReadOnly<T> = { 
  readonly [K in keyof T]: T[K] 
};
const readperson: ReadOnly<Person> = {
    name: "Tobias",
    age:18
};

readperson.age = '' // Cannot assign to 'age' because it is a read-only property.

就是这样

还不明白看看这个

type 可选的<未知> = { 
  [键 in keyof 未知]?: 未知[键] 
};

type 可选的<未知> = { 
  [键 in keyof 未知]?: 未知[键] 
};

const readperson: 可选的<Person> = {
    name: "Tobias",
};

总结下

A extends keyof B

表示 A 是B 的子类型

in 是遍历的意思

Record

type Record<K extends keyof any, T> = {
    [P in K]: T;
};

export const isObject = (val: unknown): val is Record<any, any> =>
  val !== null && typeof val === 'object'

export const isFunction = (val: unknown): val is Function =>
  typeof val === 'function'

export const isPromise = <T = any>(val: unknown): val is Promise<T> => {
  return isObject(val) && isFunction(val.then) && isFunction(val.catch)
}

又有小可爱问了:这个Record是什么意思呀

看不懂没关系下次再讲

扩展

值得注意的点

1、is 在 TS 中可以用来类型保护,缩小返回

参考链接

2、keyof , extends keyof , in keyof

泛型参考链接