跳到主要内容

· 阅读需 8 分钟
黄振敏

类型

原始类型

  • boolean: 表示布尔值 (true 或 false)。
  • number: 表示数字(支持整数和浮点数)。
  • string: 表示字符串。
  • null: 表示 null 值。
  • undefined: 表示 undefined 值。
  • symbol: 用于创建唯一值的类型。

复杂类型

  • 大整数类型: bigint
  • 数组类型:[]Array<T>
  • 对象类型:{}
  • object 类型:object, 表示非原始类型
  • Object 类型:所有类型(包括原始类型)的基类
  • 元组类型:[T1, T2, ...]
  • 枚举类型:enum
  • 函数类型:(args) => returnType
  • 类类型:class
  • 接口类型:interface
  • 泛型类型:<T>
  • 类型别名:type
  • any 类型:any
  • unknown 类型:unknown
  • void 类型:void
  • never 类型:never
  • Promise 类型:Promise<T>

联合类型

语法形式:

let value: string | number;
type Direction = "up" | "down" | "left" | "right";

使用:
只有在对联合的每个成员都有效的情况下才允许操作。

function example(value: string | number) {
// 报错 toUpperCase 不属于 number 类型上的方法
console.log(value.toUpperCase());
}

可以使用类型保护来进一步确定类型。

function example(value: string | number) {
if (typeof value === "string") {
// 这里 TypeScript 知道 value 是 string 类型
console.log(value.toUpperCase());
} else {
// 这里 TypeScript 知道 value 是 number 类型
console.log(value * 2);
}
}

当处理对象的属性时,in 运算符可以用来检查对象是否具有某个属性:

type Bird = { fly: () => void };
type Fish = { swim: () => void };

function move(animal: Bird | Fish) {
if ('fly' in animal) {
animal.fly();
} else {
animal.swim();
}
}

合并联合类型

type UnionA = 'px' | 'em' | 'rem' | '%';

type UnionB = 'vh' | 'em' | 'rem' | 'pt';

type IntersectionUnion = UnionA & UnionB; // 'em' | 'rem'

类型缩减

type URStr = 'string' | string; // 类型是 string

type URNum = 2 | number; // 类型是 number

type URBoolen = true | boolean; // 类型是 boolean

enum EnumUR {
ONE,
TWO
}

type URE = EnumUR.ONE | EnumUR; // 类型是 EnumUR

交叉类型

语法形式:

type Useless = string & number;

枚举类型

语法形式:

enum Day {
SUNDAY = 'SUNDAY',
MONDAY = 'MONDAY',
TUESDAY = 'TUESDAY',
}
提示

枚举是一种比较特殊的类型,因为它兼具值和类型于一体

数字枚举

enum Day {
SUNDAY = 1,
MONDAY,
TUESDAY,
}

会从1开始数字递增

函数类型

基本的函数类型定义:

function add(x: number, y: number): number {
return x + y;
}

函数表达式类型定义:

type Fn = (x: number, y: number) => number;
let myAdd: Fn = function(x: number, y: number): number { return x + y; };

this 定义:

function say(this: Window, name: string) {
console.log(this.name);
}

window.say = say;
window.say('hi');
const obj = {
say
};

obj.say('hi'); // ts(2684) The 'this' context of type '{ say: (this: Window, name: string) => void; }' is not assignable to method's 'this' of type 'Window'.

函数重载
根据传递的参数类型不同,函数可以有不同的行为。可以为同一个函数声明多个类型签名。

function double(value: number): number;
function double(value: string): string;

function double(value: any): any {
if (typeof value === "number") {
return value * 2;
} else if (typeof value === "string") {
return value.repeat(2);
}
}

console.log(double(5)); // 输出: 10
console.log(double("abc")); // 输出: abcabc
提示

函数重载列表的各个成员必须是函数实现的子集

函数类型谓词
语法形式:参数名 + is + 类型

function isString(value: unknown): value is string {
return typeof value === "string";
}

类型断言

语法形式:

  1. 尖括号语法:
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
  1. as 语法(推荐使用):
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
  1. ! 非空断言(不推荐,用类型守卫代替):
let someValue: string | null | undefined;
// 使用非空断言,告诉编译器 someValue 不是 null 或 undefined
console.log(someValue!.length);
提示

类型断言的操作对象必须满足某些约束关系,否则我们将得到一个 ts(2352) 错误,即从类型“源类型”到类型“目标类型”的转换是错误的,因为这两种类型不能充分重叠。
父子、子父类型之间可以使用类型断言进行转换。
anyunknown 这两个特殊类型属于万金油,因为它们既可以被断言成任何类型,反过来任何类型也都可以被断言成 anyunknown。因此,如果我们想强行“指鹿为马”,就可以先把“鹿”断言为 anyunknown,然后再把 anyunknown 断言为“马”,比如鹿 as any as 马

类型别名

语法形式:

type Age = number;
type Point = {
x: number;
y: number;
};
type ID = number | string;
提示

类型别名不能重复定义

类型守卫(缩小类型)

使用类型守卫来区分联合类型的不同成员,常用的类型守卫包括 switch、字面量恒等、typeof、instanceof、in 和自定义类型守卫。 switch

 const convert = (c: 'a' | 1) => {
switch (c) {
case 1:
return c.toFixed(); // c is 1
case 'a':
return c.toLowerCase(); // c is 'a'
}
}

字面量恒等

  const convert = (c: 'a' | 1) => {
if (c === 1) {
return c.toFixed(); // c is 1
} else if (c === 'a') {
return c.toLowerCase(); // c is 'a'
}
}

typeof

  const convert = (c: 'a' | 1) => {
if (typeof c === 'number') {
return c.toFixed(); // c is 1
} else if (typeof c === 'string') {
return c.toLowerCase(); // c is 'a'
}
}

instanceof

class Dog {
wang = 'wangwang';
}
class Cat {
miao = 'miaomiao';
}

const getName = (animal: Dog | Cat) => {
if (animal instanceof Dog) {
// Dog
return animal.wang;
} else if (animal instanceof Cat) {
// Cat
return animal.miao;
}
}

in

class Dog {
wang = 'wangwang';
}
class Cat {
miao = 'miaomiao';
}

const getName = (animal: Dog | Cat) => {
if ('wang' in animal) {
// Dog
return animal.wang;
} else if ('miao' in animal) {
// Cat
return animal.miao;
}
}

is

const isDog = function (animal: Dog | Cat): animal is Dog {
return 'wang' in animal;
}
const getName = (animal: Dog | Cat) => {
if (isDog(animal)) {
return animal.wang;
}
}

类型兼容

赋值类型/被赋值类型anyunknownobjectvoidundefinednullnever
any
unknown
object
void
undefined
null
never

泛型

· 阅读需 7 分钟
黄振敏

概述

动态规划(Dynamic Programming,简称DP)是一种算法设计思想,用于解决具有重叠子问题和最优子结构性质的问题。它通过将复杂问题分解为较小的子问题,避免重复计算相同的子问题,从而提高计算效率。
基本思想可以概括为以下几个步骤:

  1. 定义子问题:将原问题分解为多个子问题。通常,子问题的解能够组合成原问题的解。
  2. 确认状态:定义问题的状态,一般通过一个或多个变量来描述当前的子问题。例如,在求解最长公共子序列的问题中,状态可以用两个变量来表示序列的长度。
  3. 状态转移方程:找出子问题之间的关系,即状态转移方程。这些方程描述了如何从一个或多个子问题的解得到原问题的解。
  4. 初始条件和边界情况:确定动态规划的初始条件,即最基本的子问题的解,以及边界情况。
  5. 求解和优化:通过迭代或递归方式求解所有子问题,得到最终的解。

· 阅读需 4 分钟
黄振敏

概述

比起 antv/x6 的 v1版本,antv/x6 v2 提升了渲染性能(异步),解决了 v1 时期存在的 react 节点挂载问题。

依赖调整

业务模块的包(hmde/hlod)不再单独安装 antd/x6 依赖,全部抽离到公共模块(apaas)。统一让公共模块进行 x6 打包,业务模块采用联邦的方式进行引入。
新的引入方式如下:

import { Graph } from '@apaas/components/AntvX6';
import { Dnd } from '@apaas/components/AntvX6/plugins';

如需补充导出,到公共模块对应的组件下导出,然后在子模块导入使用

· 阅读需 8 分钟
黄振敏

阅读本篇需要对二叉树及其结构有基本的了解。

概念

堆一定是一颗完全二叉树, 按排序大小规则主要分为 2 种类型————最大堆最小堆

  • 最大堆:根节点的值大于等于左右子节点的值。
  • 最小堆:根节点的值小于等于左右子节点的值。
node

总结:最大堆和最小堆的根本区别在于根节点的最值情况。

Heap 类设计

在堆算法解题中,一般都需要设计一个 Heap 类,用于实现最大堆最小堆的通用操作。

· 阅读需 23 分钟
黄振敏

X6 是 AntV 旗下的图编辑引擎,提供了一系列开箱即用的交互组件和简单易用的节点定制能力,方便我们快速搭建流程图、DAG 图、ER 图等图应用。

本文适用于有一定 antv/x6 使用基础的开发者。

实现效果

alt 实现效果

实现效果示意图

业务对象 ER 图用来表示不同领域下不同业务对象之间的关系,如上图所示:

  • 一张卡片代表一个节点(业务对象),每个节点下有多个字段。
  • 节点间关系,使用带有向箭头的边表示。不同关系会有不同的边颜色对应展示。
  • 点击选中某个节点,与该节点所关联的边都会加粗高亮。
  • 存在操作按钮,可动态切换每个节点内展示的关联/非关联字段。
  • 可通过按钮操作业务对象的增删改查。
  • 可拖拽节点,但是不能自建连线。
  • 自动布局。

· 阅读需 6 分钟
黄振敏

三类空间

  • 命名空间
    声明的分类,不是具体的实体。一个命名空间内可以包含值或类型。对命名空间建立应用需要使用 import 语句,如果使用 const、let 或 var 会使新的实体不再具有命名空间的属性。编译成 JS 代码后会被清除。
  • 类型空间
    描述一个值的类型。常见有 type、interface、class、enum 等。编译成 JS 代码后会被清除。
  • 值空间
    真实可参与运算的值。常见有变量、对象、数组、class、enum 等。编译成 JS代码后会被保留。

操作符

  • typeofinstanceof:返回更详细的类型
  • keyof:返回一个对象的属性名称的字符串数组
  • O[K]:返回对象 K 的值
  • [K in O]:逐一映射 O 的类型
  • extends:泛型、类型别名、函数参数、联合类型的条件类型判断
  • infer:定义类型变量
  • readonly :只读
  • ?:可选
  • -?:去除类型的可选属性
  • !: 非空断言
  • =: 泛型的默认值
  • as: 类型断言
  • is: 类型谓词,辅助类型推断

· 阅读需 22 分钟
黄振敏

基础使用

import React, { useState, useCallback, useRef } from "react";
import MonacoEditor from "react-monaco-editor";
import * as monaco from "monaco-editor";

const Demo = () => {
const [value, setValue] = useState("");

const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>(); // 编辑器实例
const monacoRef = useRef<typeof monaco>(); // monaco 实例

// 获取编辑器实例
const editorDidMountHandle = useCallback(
(editor: monaco.editor.IStandaloneCodeEditor, monacoIns: typeof monaco) => {
editorRef.current = editor;
monacoRef.current = monacoIns;
},[]);

return (
<MonacoEditor
language="javascript"
height="100%"
theme="vs"
value={value}
onChange={setValue}
options={{
roundedSelection: false,
cursorStyle: "line",
wordWrap: "on",
}}
editorDidMount={editorDidMountHandle}
/>
);
};

editorInstancemonacoInstance 的区别:

  • editorInstance:主要作用于编辑器上操作的方法,例如编辑器写入操作等。
  • monacoInstance:主要是编辑器语言相关的内容,例如变量提示、鼠标悬浮提示等。

· 阅读需 7 分钟
黄振敏

回调地域问题

传统的异步编程方式通过回调函数,处理异步任务到期的执行问题。而异步任务之间如果产生依赖关系,就会陷入“回调地域”问题。

callback

状态

Promise 的状态不可逆
有3个状态

  • Pending
  • Resolved
  • Rejected

静态方法

  • Promise.resolve
  • Promise.reject
提示

只接收第一个参数