Typescript 中泛型的高级用法

typescript-generics.png

继承类型

extends关键字不仅可以用在类和接口上,还可以用在类型上。

function myGenericFunction<T extends string>(arg: T): T {
    return arg;
}

条件类型

type MyType<T> = T extends string ? boolean : number;

映射类型

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
}

他允许我们在T类型集合上进行迭代。

通用映射类型

// 全键可选
type Partial<T> = {
    [P in keyof T]?: T[P];
};

// 全键必需
type Required<T> = {
    [P in keyof T]-?: T[P];
};

// 全键只读
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

我们还可以写出更多的通用映射类型,如:

// 可为空类型
type Nullable<T> {
    [P in keyof T]: T[P] | null
}

// 包装一个类型的属性
type Proxy<T> = {
    get(): T
    set(value: T): void
}
type Proxify<T> = {
    [P in keyof T]: Proxy<T[P]>
}
function proxify(o: T): Proxify<T> {
    // ...
}
let proxyProps = proxify(props)

元组类型

var user: [number, string] = [1, "Steve"];
var employee: [number, string][];
employee = [[1, "Steve"], [2, "Bill"], [3, "Jeff"]];

交叉类型 ( & )和联合类型 ( | )

  • 交叉类型是将多个类型合并为一个类型。
  • 联合类型,如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员。
type landAnimal = {
  name: string
  canLiveOnLand: true
}
type waterAnimal = {
  name: string
  canLiveInWater: true
}
// 交叉类型:两栖动物
type amphibian = landAnimal & waterAnimal
let toad: amphibian = {
  name: 'toad',
  canLiveOnLand: true,
  canLiveInWater: true,
}

全集和空集

  • any 类型,泛指一切可能的类型,对应全集。
  • never 类型对应空集。任何值,即使是 undefined 或 null 也不能赋值给 never 类型。

类型保护与区分类型

类型断言

类型断言有两种语法 <类型>值值 as 类型

使用类型保护

使用类型断言,需要多次判断十分麻烦。所以使用类型保护
这种param is SomeType的形式,就是类型保护,它用来明确一个联合类型变量的具体类型
类型谓词 谓词为 parameterName is Type这种形式,parameterName必须是来自于当前函数签名里的一个参数名。

  • typeof类型保护只能用于 number, string, boolean, symbol(只有这几种类型会被认为是类型保护)
  • instanceof类型保护用于类

参考文章:
https://www.freecodecamp.org/news/typescript-curry-ramda-types-f747e99744ab/

Webview 远程调试

Android 使用 ADB 调试

通过 Homebrew 安装 android-platform-tools
brew cask install android-platform-tools
使用connect命令连接设备:
adb connect <ip>:19129

然后开启手机上的USB调试功能。
访问Chrome://inspect,点击Inspect,弹出开发者工具进行调试。

adb disconnect 断开所有连接。

常见问题

  • 如果会出现白屏或者 404 错误,这是因为Chrome需要访问 https://chrome-devtools-frontend.appspot.com,来选择要使用兼容的 devtools。所以将该域名加入代理即可。
  • 如果调试工具界面错乱,则说明调试工具的版本过低(Chrome 63 后移除了 /deep/ 选择器),需要下载一个低版本Chrome,这里推荐下载Chromium。

IOS

在移动端,启用 设置 -> Safari 浏览器 -> 高级 -> Web 检查器,然后使用数据线链接,或下载[Safari Technology Preview] 无线远程调试(因为稳定版 Safari 不支持)。

参考文章:
https://saubcy.com/2019/01/13/chrome-remote-devices-window-breaks/

FontCut ✂ 字体裁剪Chrome插件

FontCut.jpg

基于opentype.js 实现了一个字体裁剪Chrome插件,能够预览字体并导出otf和woff格式的字体子集。

目前font-carrier,fontkit,fontmin等等字体裁剪方案只能借助服务端能力,着实不便。其实这些项目大多也是基于opentype.js的。
opentype.js 能够解析 ttf otf woff 三种文件格式解析为一个 font 类,本身提供了在浏览器运行的能力。

捕获.PNG

字体科普

ttf 与 otf

OpenType字体的Outline描述方法主要有TrueType和Postscript,前者的字库文件后缀名一般为ttf,后者的后缀名一般为otf。
OTF通过提供许多TTF无法提供的功能来扩展TTF。例如,OTF的格式允许最多存储65,000个字符。OTF和TTF之间的主要区别是高级排版功能。OTF具有诸如连字和替代字符(也称为字形)之类的装饰。
ligatures-640x72.png

WOFF

摘录维基百科中的介绍:

Web开放字体格式(Web Open Font Format,简称WOFF)是一种网页所采用的字体格式标准。此字体格式发展于2009年,由万维网联盟的Web字体工作小组标准化,现在已经是推荐标准。此字体格式不但能够有效利用压缩来减少文件大小,并且不包含加密也不受DRM(数字著作权管理)限制。

WOFF 1.0使用zlib压缩,文件大小一般比TTF小40%。而WOFF 2.0使用Brotli压缩,文件大小比上一版小30%。

CSS @font-face

字体格式可以选择以下几种:"woff", "woff2", "truetype", "opentype", "embedded-opentype", and "svg"。(IOS 不支持 woff2)

@font-face {
  font-family:'Awesome Font';
  font-style: normal;
  font-weight: 400;
  src: local('Awesome Font'),
       url('/fonts/awesome.woff2') format('woff2'),
       url('/fonts/awesome.woff') format('woff'),
       url('/fonts/awesome.ttf') format('truetype'),
       url('/fonts/awesome.eot') format('embedded-opentype');
}

加载顺序

字体变体的指定顺序很重要。 浏览器将选取其支持的第一种格式。所以如果需要兼容的浏览器使用 WOFF2,则应将 WOFF2 声明置于 WOFF 之上,依此类推。

字体的加载

字体延迟加载带有一个可能会延迟文本渲染的重要隐藏影响:浏览器必须构建渲染树(它依赖 DOM 和 CSSOM 树),然后才能知道需要使用哪些字体资源来渲染文本。 因此,字体请求的处理将远远滞后于其他关键资源请求的处理,并且在提取资源之前,可能会阻止浏览器渲染文本。

font-crp.png

如果我们知道页面用了那些文字,我们还可以利用新功能:<link rel="preload">。该功可以在关键渲染路径中提早触发对网络字体的请求,而不必等待创建 CSSOM。

<head>
  <!-- Other tags... -->
  <link rel="preload" href="/fonts/awesome-l.woff2" as="font">
</head>

项目地址

GitHub 地址

后续开发计划

使用otfcc或其他C语言字体解析库编译成 webassembly 加快解析速度。

参考文档:

一种使用倒排索引和位图的快速SKU选择方法

使用所有有货的SKU组合生成该SKU到该组合的索引。如 [A-B,B-C] 即生成 {A:[1],B:[1,2],C:[2]}
映射到视图时只需要判断当前选中项的SKU索引的交集(默认为全集),例如选中B,则[1,2,3][1,2]的交集[1,2],然后依次遍历SKU,判断SKU对应的索引与当前选中索引是否有交集。如果有则可选,没有则置灰。

索引可以使用位图数据结构,在判断是否为交集使用位运算提升性能。

   const sku = [];
    const revertIndex = {};
    const size = 8;
    // 随机sku
    for (let i = 0; i < size; i++) {
        sku[i] = new Set();
        for (let j = 0; j < size; j++) {
            const v = j * size + RandomNumBoth(0, size);
            sku[i].add(v)
        }
    }

    for (let i = 0; i < sku.length; i++) {
        sku[i].forEach((e) => {
            if (!revertIndex[e]) revertIndex[e] = 0;
            revertIndex[e] |= 1 << i
        })
    }

判断是否选中使用 (revertIndex[(line-1)*size + e -1] & union)

下面是一个简单的例子,取消选择和选中逻辑是一样的,所以就暂时没写:

See the Pen SKU Demo by zhangchen (@zhangchen915) on CodePen.