成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

DeepKit —— 賦予 TypeScript 更多可能性

開發 前端
傳統開發上,Javascript 基本沒有提供任何類型保護,所有的類型錯誤都需要在運行時才能發現,而TypeScript 為開發者提供了一套靜態類型檢查的方案,它提倡開發者在源碼中主動聲明類型信息,并與對應的變量和操作相匹配,并在編譯階段進行檢查,類型相關的錯誤在編譯時就暴露出來,一方面使代碼更規范了,一方面也極大程度地規避了許多代碼錯誤,提高了代碼的健壯性。

本文為來自飛書 aPaaS Growth 研發 團隊成員的文章,已授權 ELab 發布。

aPaaS Growth 團隊專注在用戶可感知的、宏觀的 aPaaS 應用的搭建流程,及租戶、應用治理等產品路徑,致力于打造 aPaaS 平臺流暢的 “應用交付” 流程和體驗,完善應用構建相關的生態,加強應用搭建的便捷性和可靠性,提升應用的整體性能,從而助力 aPaaS 的用戶增長,與基礎團隊一起推進 aPaaS 在企業內外部的落地與提效。

?背景?

之前在技術需求中曾調研了基于 TypeScript 的數據校驗方案,其中調研了一個叫 Deepkit 的第三方庫,可以將 TypeScript 的類型信息保留到運行時進行消費。

圖片

?TypeScript 帶來的?

傳統開發上,Javascript 基本沒有提供任何類型保護,所有的類型錯誤都需要在運行時才能發現,而TypeScript 為開發者提供了一套靜態類型檢查的方案,它提倡開發者在源碼中主動聲明類型信息,并與對應的變量和操作相匹配,并在編譯階段進行檢查,類型相關的錯誤在編譯時就暴露出來,一方面使代碼更規范了,一方面也極大程度地規避了許多代碼錯誤,提高了代碼的健壯性。

圖片

TypeScirpt 擁有完備的類型系統。但很可惜,它在這方面的能力在運行時幾乎完全不存在。TypeScript Compiler在編譯源碼時會刪除類型信息,不對運行時造成任何開銷。

但其實在許多場景下,運行時的類型信息都是極具價值的!

?為什么需要運行時類型?

為什么我們需要運行時的類型信息呢?讓我們看看下面兩個場景

數據校驗

數據校驗并不是局限于傳統前端所關注的表單校驗,需要數據校驗的場景數不勝數,比如:

在編寫服務的時候,若我們需要實現一個接口。對于我們來說,傳入的參數是未知的,我們永遠不知道業務方會給我傳來什么奇奇怪怪的參數。如果我們不對參數進行校驗的話,后面的代碼邏輯隨時可能崩潰。而參數校驗自然就需要在運行時消費參數的類型定義信息。

數據庫,一張表中所有字段的類型都是有嚴格定義的,所以在數據寫入數據庫時,需要校驗寫入的數據是否符合字段的類型定義,這也需要運行時的類型信息。

序列化與反序列化

序列化是將數據類型轉換為適合傳輸或存儲的格式的過程。反序列化是撤消此操作的過程,這個過程需要保證是無損的。對于前端開發者來說,接觸的最多的應該就是 JSON.parse()? 和 JSON.stringify() 這兩個方法。在簡單場景下,用這兩個方法做序列化和反序列化可能沒有問題,但是在復雜場景中就不一定了,因為這兩個方法并不能保證數據是無損的。

例如下面這個場景

const date = new Date();
const dateString = JSON.stringify(date);//"2022-11-02T17:49:03.240Z"
const dateJson = JSON.parse(dateString);//"2022-11-02T17:49:03.240Z"

對于日期類型的數據,先用 JSON.stringify(date) 將其序列化成了適合傳輸的格式,再用JSON.parse(dateString) 反序列化,發現日期這個類型在過程中已經丟失,最后反序列化的結果為一個字符串,這顯然是不符合預期的。因此,在序列化和反序列化的過程中,類型信息也十分重要。

而 DeepKit 使將 TypeScript 類型保留到運行時成為現實。

?快速開始?

官方文檔站:https://deepkit.io/

前置

使用 DeepKit 需要安裝兩個包:

  • @deepkit/type:提供運行時可以使用的方法
  • @deepkit/type-compiler:類型編譯器,介入TypeScript 編譯流程,保留類型信息。可以放在package.json? 的devDependencies中,因為這個類型編譯器只需要編譯階段使用。
npm install --save @deepkit/type
npm install --save-dev @deepkit/type-compiler

然后需要在 tsconfig.json? 中配置 "reflection": true? 。如果需要使用裝飾器,還需要加入"experimentalDecorators": true 參數

// tsconfig.json
{
"compilerOptions":{
"module":"CommonJS",
"target":"es6",
"moduleResolution":"node",
"experimentalDecorators":true
},
"reflection":true
}

類型信息

DeepKit 定義了兩種用于描述運行時的類型信息的數據結構,分別是類型對象和反射類。

類型對象

使用 typeOf 方法可以快速獲取某個類型對應的類型對象。

import { typeOf } from '@deepkit/type';
type Title<T> = T extends true ? string : number;

typeOf<Title<true>>();
//Type {kind: 5, typeName: 'Title', typeArguments: [{kind: 7}]}

從上面的例子中,我們可以看到一個類型對象的基本數據結構(當然,這還不是它的全貌)。詳細的類型對象定義:https://github.com/deepkit/deepkit-framework/blob/feature/autotype/packages/type/src/reflection/type.ts#L21-L452

  • kind:ReflectionKind,表示傳入的類型。例子中對應 Title 的類型
  • typeName:string,如果用到了類型別名,會返回這個字段,標識該類型
  • typeArguments:當我們用了泛型時,傳遞進去的類型信息也會保留到類型對象中,會返回typeArguments 字段中記錄的就是對應的類型信息。
enum ReflectionKind {
never, //0
any, //1
unknown, //2
void, //3
object, //4
string, //5
number, //6
boolean, //7
symbol, //8
bigint, //9
null, //10
undefined, //11

//... and even more
}

反射類

反射類多用于 類/接口/對象類型等等比較復雜的場景

import { ReflectionClass } from '@deepkit/type';

interface User {
id: number;
username: string;
}

const reflection = ReflectionClass.from<User>();

reflection.getProperty('id'); //ReflectionProperty,記錄id類型信息

reflection.getProperty('id').name; //'id'
reflection.getProperty('id').type; //{kind: ReflectionKind.number}
reflection.getProperty('id').isOptional(); //false
reflection.removeProperty('id');
reflection.getProperty('id');//Error: No property id found in User

對于復雜場景,我們可以通過 ReflectionClass.from 方法得到類型對應的放射類實例 ReflectionClass ,通過調用ReflectionClass中的方法可以獲取更深層次的類型信息,也可以對類型信息做一些操作。

驗證

需要數據驗證的場景數不勝數,接口參數校驗,數據庫實現等都高度依賴數據校驗,以此保證數據的安全性。

DeepKit 提供了is和validate兩個函數,用于校驗一個值是否符合類型定義。

interface People {
name: string
age: number,
info?: {
address?: string,
phone: number
}
}

const peopleA = {
name: 'Jack',
age: 20,
}

const peopleB = {
name: 'Peter',
age: 18,
info: {}
}

is<People>(peopleA)//true
is<People>(peopleB)//false

is 函數接收類型信息,并對參數中的數據進行校驗,返回一個布爾值。如上面的例子,定義了一個 People 的 interface,并對 peopleA 和 peopleB 兩個數據進行校驗,可以看出 peopleA 是符合 People 的 定義的,所以返回is<People>(peopleA)?會返回 true 。peopleB 中的 info 屬性缺少了必填的 phone 字段,因此is<People>(peopleB) 會返回 false 。

validate<People>(peopleA)//[]

validate<People>(peopleB)
// [{
// path: 'info.phone',
// code: 'type',
// message: 'Not a number'
// }]

validate 函數和 is 函數的用法類似,區別是 validate 函數并不是返回一個布爾值 ,而是一個包含錯誤信息的數組。

path:錯誤路徑,指向出錯的具體屬性

code:錯誤類型,目前好像只有type 一種。

message:具體的錯誤信息。

序列化

DeepKit 中 serialize/deserialize 兩個方法,為用戶提供了序列化/反序列化的能力

import { serialize } from '@deepkit/type';

class MyModel {
id: number = 0;
created: Date = new Date;

constructor(public name: string) {
}
}

const model = new MyModel('Peter');

const jsonObject = serialize<MyModel>(model);
//{
// id: 0,
// created: 2022-11-02T17:49:03.240Z,
// name: 'Peter'
//}

serialize 方法接收類型信息和需要序列化的數據,將數據序列化為符合類型定義的JSON對象。

const myModel = deserialize<MyModel>({
id: 5,
created: 'Sat Oct 13 2018 14:17:35 GMT+0200',
name: 'Peter',
});

is<Date>(myModel.created)// true

deserialize 方法接收類型信息和需要反序列化的數據,將數據反序列化為符合類型信息定義的數據。代碼中的 created 字段會被反序列化為 Date 字段。

類型裝飾器

一句話概括裝飾器:裝飾器本質上就是一個函數,可以在運行時對被裝飾對象進行自定義的加工處理。

DeepKit 中提供了一套類型裝飾器,這里的類型裝飾器和 TypeScript 的裝飾器并不相同,TypeScript 多用于對類的裝飾,類型裝飾器顧名思義是對類型的裝飾。這些類型裝飾器可以被當作一個正常的 TypeScript 類型使用。

舉一個簡單的例子

import { integer } from '@deepkit/type';

// case 1
type count = integer;
is<count>(1) // true
is<count>(1.1) // false

我們對定義 count 類型為 integer(整型),可以看到,1.1這個浮點數類型并沒有通過校驗。

除此之外,DeepKit 還實現了如 PrimaryKey(主鍵),maxLength/minLength(最小/最大長度)等功能的類型裝飾器。我們可以把這些類型裝飾器看作對于 TypeScript 類型的拓展,這些類型裝飾器使 TypeScript 能夠實現數據庫級別的類型定義。也正是基于這套拓展后的運行時類型,驗證和序列化可以有更多的約束,DeepKit 也實現了一套高性能的 ORM 。

?More?

@deepKit/type 給我們提供了一套運行時調用類型信息的方案。除此之外,DeepKit 的作者還基于類型信息和反射機制實現了更多的能力。

  • 事件系統:@deepkit/events
  • HTTP 庫:@deepkit/http
  • RPC服務:@deepkit/rpc
  • 數據庫ORM:@deepkit/orm
  • 模版引擎:@deepkit/templat ,但與react不兼容
  • 大一統框架:@deepkit/framework ,集成了上述能力的 node 框架

?如何保證性能?

為了盡量壓縮運行時的額外開銷,DeepKit 的作者做出了不少優化。

類型緩存

在未使用泛型的情況下,DeepKit 會對使用到的類型對象進行緩存

//  case1
type MyType = string;

typeOf<MyType>() === typeOf<MyType>(); //true

// case2
type MyType<T> = T;

typeOf<MyType<string>>() === typeOf<MyType<string>>();//false

可以看到,對于 case1 ,Mytype 對應的類型對象會被緩存,因此兩次typeOf<MyType>()? 的結果相等;但是對于泛型來說,我們無法確定傳入的 T 具體是什么類型(理論上會有無限種),因此不會結果進行緩存,每次都會創建一個新的類型對象。

類型編譯器

圖片

DeepKit 的核心原理是一個類型編譯器,它會介入TypeScript 的編譯流程,保留類型信息, 在這個過程中,Deepkit 的類型編譯器會讀取源碼中的類型信息,產生相關的字節碼(為了使它盡可能小),并將其插入 AST 中,將其轉化為另一個包含這些字節碼信息的 TypeScript AST。

在運行時,DeepKit 會有一個迷你虛擬機,負責解析和執行這些字節碼,最后會返回一個類型對象。

更詳細的原理可以參考:https://github.com/microsoft/TypeScript/issues/47658

在 DeepKit 官方提供的性能圖中,可以看到 DeepKit 在數據讀寫上的表現是比較優秀的,這也歸功于 DeepKit 提供的 運行時類型信息,這種預先知曉類型信息的機制可以使 序列化/驗證等更加快速高效。

圖片

?總結?

DeepKit 是市場上第一個在 JavaScript 運行時提供全套 TypeScript 類型的解決方案。它使前端/服務端可以共用一套TypeScript定義的數據模型,并且使用基于 TypeScript 實現的一套反射機制。

但它依舊存在一些不足,比如 不支持外部類型,若代碼中使用的類型信息來自第三方,且第三方庫也沒有經過 deepkit 的類型編譯器的話,外部類型的類型信息在運行時也會全部丟失。

官方文檔站:https://deepkit.io/

?一些討論?

在TypeScript的倉庫中,其實已經有許多人提出了issue,對在運行時保留Typescript的類型信息提出了自己的設想。可以看出,在基于 TypeScript支持動態類型這件事情上,是有需求的,但是 TypeScript 始終是保持保留意見,并沒有實質去支持相關能力。

圖片

個人的看法,根本上是和 TypeScript 的設計目標[1] 掛鉤, TypeScript 官方團隊并不希望 TypeScript 會對運行時造成額外的開銷,并且希望生成的 JavaScript 盡量純凈。TypeScript 官方團隊 的保守嚴謹造就了 TypeScript 的成功。可能正因如此,TypeScript 官方團隊才一直對支持運行時類型持保守態度。

?參考文獻?

https://deepkit.io/ https://github.com/microsoft/TypeScript/issues/47658

責任編輯:武曉燕 來源: ELab團隊
相關推薦

2017-07-21 16:40:29

網易云場景專屬云

2019-10-09 17:28:08

程序員人生第一份工作技術

2021-06-17 11:14:22

云計算云原生

2024-12-06 08:20:26

2021-09-29 18:59:42

戴爾

2011-04-20 10:07:15

2018-03-02 11:38:11

2012-06-04 13:28:51

AndroidChrome OS

2011-04-18 13:43:42

2023-10-27 14:25:26

組件庫無限可能性

2016-09-21 09:16:55

Qlik

2021-02-20 12:04:51

比特幣區塊鏈美元

2020-05-15 13:42:03

物聯網人工智能軍事物聯網

2013-03-19 11:13:14

Google廣告SXSW

2019-04-22 08:57:46

硅谷996ICU

2020-08-11 09:38:40

微信蘋果美國

2009-03-11 18:27:04

Windows 7商業版

2011-04-18 13:47:59

ECC私鑰
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧洲妇女成人淫片aaa视频 | 国产在线一区二 | 国产一区二区视频在线观看 | 91免费观看视频 | 颜色网站在线观看 | 色婷婷综合久久久中文字幕 | 国产精品不卡视频 | 中文字幕亚洲一区二区三区 | av一区二区在线观看 | 精品久久久久久亚洲综合网站 | 欧美a级成人淫片免费看 | 日日日日日日bbbbb视频 | 免费黄色网址视频 | 色又黄又爽网站www久久 | 日韩黄a | 在线午夜 | 久久久久久国产免费视网址 | 午夜精品一区二区三区在线视频 | 中文字幕 视频一区 | 色偷偷888欧美精品久久久 | 精品国产乱码久久久久久闺蜜 | 国产精品欧美一区二区三区不卡 | 精品国产三级 | 欧美日韩成人影院 | 久久久久精 | 亚洲毛片一区二区 | 欧美福利一区 | 一区二区三区免费观看 | 亚洲综合色视频在线观看 | 香蕉91 | 国产中文原创 | 欧美福利一区 | 在线看av的网址 | 日韩福利在线观看 | 天天干天天插天天 | 男女国产网站 | 日韩精品免费 | 欧美精品在线一区 | 亚洲国产成人av好男人在线观看 | 81精品国产乱码久久久久久 | 欧美在线视频一区二区 |