TypeScript學(xué)習(xí)輕松玩轉(zhuǎn)類型操作
引言
本文的目的是探索 TypeScript 類型操作的基礎(chǔ)知識,先介紹 typeof 、keyof、extends、infer 等操作關(guān)鍵詞的使用方式(作為基礎(chǔ));然后介紹TS提供的通用類型工具(詳細(xì)介紹官方源碼具體實(shí)現(xiàn));這兩個(gè)部分熟練掌握,我們就掌握TS體操的核心內(nèi)容了,剩下的就是考慮根據(jù)類型目標(biāo)情況如何選擇類型操作順序。歡迎成為體操小能手~
typeof
typeof 在 JS 中用于獲取一個(gè)變量的基礎(chǔ)數(shù)據(jù)類型,在 TS 中 typeof 用于獲取運(yùn)行時(shí)變量或者屬性的類型。在TS代碼中需要注意這兩種用法之間的差異,使用的情況不同的語義環(huán)境下有不能的表現(xiàn)形式。
typeof 以JS的判斷變量類型時(shí)工作時(shí),返回值是一個(gè)字符串內(nèi)容是其中之一
("string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function")。
const s = "hello"; let n = typeof s; // n 值是 "string",n 類型是 let n: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" let m: typeof s; // let m: stringconst s = "hello"; let n = typeof s; // n 值是 "string",n 類型是 let n: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" let m: typeof s; // let m: string
typeof 作為TS訪問運(yùn)行時(shí)值讀取類型時(shí),這個(gè)類型經(jīng)過讀取 type context 進(jìn)行類型推導(dǎo)得出【返回的是一個(gè)TS的類型值,內(nèi)容來自:自定義的類型修飾、無類型修飾時(shí)默認(rèn)推導(dǎo)類型、強(qiáng)制類型轉(zhuǎn)換后的類型結(jié)果】。
const point = { x:1,y:1 }; // 沒有指定類型,TS 默認(rèn)類型推理推導(dǎo)出合適類型 type PointType = typeof point; /* 等價(jià)于 type PointType = { x: number; y: number; } */ const person = { name: 'xxx' } as any; // as 強(qiáng)制類型轉(zhuǎn)換為 any, 使用強(qiáng)制類型轉(zhuǎn)化的類型作為結(jié)果 type PersonType = typeof person; // 等價(jià)于 type PersonType = any type Car = { drive: ()=>void; name?: string; } const car: Car = { drive:()=>{} } // 使用了指定的類型作為修飾,結(jié)果返回類型為指定的類型 type CarType = typeof car; /* 等價(jià)于 type CarType = { drive: () => void; name?: string | undefined; } */const point = { x:1,y:1 }; // 沒有指定類型,TS 默認(rèn)類型推理推導(dǎo)出合適類型 type PointType = typeof point; /* 等價(jià)于 type PointType = { x: number; y: number; } */ const person = { name: 'xxx' } as any; // as 強(qiáng)制類型轉(zhuǎn)換為 any, 使用強(qiáng)制類型轉(zhuǎn)化的類型作為結(jié)果 type PersonType = typeof person; // 等價(jià)于 type PersonType = any type Car = { drive: ()=>void; name?: string; } const car: Car = { drive:()=>{} } // 使用了指定的類型作為修飾,結(jié)果返回類型為指定的類型 type CarType = typeof car; /* 等價(jià)于 type CarType = { drive: () => void; name?: string | undefined; } */
typeof 的作用就是從JavaScript運(yùn)行時(shí)變量值上提取出對應(yīng)的TS類型,在 TS 體操中它是基礎(chǔ)的類型操作。和 keyof 、 Omit、Partial、Record 等搭配起來非常好用。常用于:類型收窄(讀取運(yùn)行時(shí)特定字段的類型,用于判斷)做類型守衛(wèi)。
keyof
keyof 用于提取一個(gè)對象類型的屬性名,返回一個(gè)字符串或數(shù)字字面量的聯(lián)合類型。只有一個(gè)屬性時(shí)返回字符串,多個(gè)屬性時(shí)返回聯(lián)合類型 ( 'xx' | 'yy'
),映射類型或條件類型下返回值會攜帶特殊的類型聯(lián)合值。
普通對象類型 keyof 提取屬性,interface 也能正常提取。注意不要對普通類型【string、number、聯(lián)合類型等】使用 keyof 。
const point = { x:1,y:1 }; type PointType = typeof point; const person = { name: 'xxx' } as any; type PersonType = typeof person; type PointKeysType = keyof PointType; // 等價(jià)于 type PointKeysType = "x" | "y" const point = { x:1,y:1 }; type PointType = typeof point; const person = { name: 'xxx' } as any; type PersonType = typeof person; type PointKeysType = keyof PointType; // 等價(jià)于 type PointKeysType = "x" | "y"
映射類型使用 keyof 時(shí)提取不到屬性,此時(shí)默認(rèn)返回可以作為對象屬性值的類型聯(lián)合 string | number | symbol
。映射類型為每個(gè)屬性指定同樣的類型,一般用于同一類型的注冊表對象類型定義。
type ToolsType = { [k:string]: Function } type ToolsKeys = keyof ToolsType; // string | number // 將一個(gè)類型T上的所有屬性值作為新類型的 key 構(gòu)建新類型 type OptionsFlags<T> = { [k in keyof T]: boolean } type CarOpFlag = keyof OptionsFlags // 這里寫法不好,只做個(gè)演示。 等價(jià)于:type CarOpFlag = string | number | symbol type ToolsType = { [k:string]: Function } type ToolsKeys = keyof ToolsType; // string | number // 將一個(gè)類型T上的所有屬性值作為新類型的 key 構(gòu)建新類型 type OptionsFlags<T> = { [k in keyof T]: boolean } type CarOpFlag = keyof OptionsFlags // 這里寫法不好,只做個(gè)演示。 等價(jià)于:type CarOpFlag = string | number | symbol
keyof 的原理是基于索引類型查詢(Index Type Query)和索引訪問操作符(Index Access Operator)的組合。當(dāng)我們使用 keyof 時(shí),它會對指定的類型進(jìn)行索引類型查詢,返回該類型的所有鍵組成的聯(lián)合類型。
extends
extends 關(guān)鍵詞用于類型之間的擴(kuò)展和約束,主要有以下功能。
一、用于擴(kuò)展一個(gè)類型或接口,使其繼承另一個(gè)類型或接口的成員。通常用于接口繼承接口,類繼承父類
interface User { name: string; } interface Student extends User { age: number; } const s:Student = { age: 11, name: 'xx' }; interface User { name: string; } interface Student extends User { age: number; } const s:Student = { age: 11, name: 'xx' };
Student 使用 extends 繼承 User 接口的 name 屬性并新增了新的 age 屬性。
二、泛型約束,用于約束泛型的類型范圍
指定泛型參數(shù)必須滿足某些條件
interface User { name: string; } interface Student extends User { age: number; } function registeredStudent<T extends User>(user: T) { // ... } interface User { name: string; } interface Student extends User { age: number; } function registeredStudent<T extends User>(user: T) { // ... }
限制 T 的類型最少要滿足 User 類型的條件也就是必須有 name 屬性并且值是 string 。TS 的類型校驗(yàn)屬于是鴨式變形【長得像鴨子,叫起來也像鴨子,那就是鴨子,才不管是不是老鼠脖子】,而不是 Java 這類強(qiáng)類型語言的判等。
三、類型條件,在類型條件中用于根據(jù)類型關(guān)系推導(dǎo)條件類型的結(jié)果。
type GetReturnType<T> = T extends (...args: any[]) => infer Return ? Return : never; type HandleType = (msg: string) => number | string; type returnType = GetReturnType<HandleType> // 等價(jià)于 type returnType = string | numbertype GetReturnType<T> = T extends (...args: any[]) => infer Return ? Return : never; type HandleType = (msg: string) => number | string; type returnType = GetReturnType<HandleType> // 等價(jià)于 type returnType = string | number
這里的 extends 是三目運(yùn)算符的判斷條件,表示:如果T類型符合 (...args: any[]) => any 這個(gè)類型那么條件成立返回T類型的返回值類型,否則返回 never。infer 關(guān)鍵詞用于在條件類型中提取部分類型保存到對應(yīng)變量,infer詳細(xì)請往下看。
infer
infer 關(guān)鍵字用于條件類型中的類型推斷,它的實(shí)現(xiàn)原理是基于TS 條件類型和推斷機(jī)制。功能是允許我們在條件類型中聲明一個(gè)類型變量,用于在條件類型的真分支中推斷出一個(gè)具體的類型。編譯器會根據(jù)已知的類型信息進(jìn)行類型推斷,將推斷出的類型賦值給 infer 聲明的類型變量。對于TS體操來說,這個(gè)是基礎(chǔ)。以下是 TypeScript 官方給我們提供的通用類型工具,基于 infer 和 extends 條件類型實(shí)現(xiàn)非常簡單。
// TypeScript 官方提供的一些類型操作工具源碼 /** * Obtain the parameters of a function type in a tuple */ type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never; /** * Obtain the parameters of a constructor function type in a tuple */ type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never; /** * Obtain the return type of a function type */ type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any; /** * Obtain the return type of a constructor function type */ type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any; // TypeScript 官方提供的一些類型操作工具源碼 /** * Obtain the parameters of a function type in a tuple */ type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never; /** * Obtain the parameters of a constructor function type in a tuple */ type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never; /** * Obtain the return type of a function type */ type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any; /** * Obtain the return type of a constructor function type */ type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;
TypeScript 提供處理類型的工具類型
隨著 TS 各個(gè)版本不斷補(bǔ)充,截止現(xiàn)在支持 22 種工具類型用于處理類型轉(zhuǎn)換。由于比較多,我們將按照分類進(jìn)行介紹。常說的TS體操實(shí)際就是掌握了這些工具類型,甚至我們可以自己定義一些通用的類型工具,然后按照要求進(jìn)行類型之間的轉(zhuǎn)換比如:將下劃線命名轉(zhuǎn)換成小駝峰命名、遞歸的提取指定類型的key等等。
以下是TypeScript 官方提供的類型工具支持。
Awaited<T> 提取異步 Promise 鏈上的返回值類型
用法
interface User { name: string; } type RequestUser = (id: number) => Promise<User>; // 從返回值類型中提取到,Promise 里面的具體類型 type ResultType = Awaited<ReturnType<RequestUser>>interface User { name: string; } type RequestUser = (id: number) => Promise<User>; // 從返回值類型中提取到,Promise 里面的具體類型 type ResultType = Awaited<ReturnType<RequestUser>>
源碼實(shí)現(xiàn),遞歸的從 Promise 鏈上讀取返回值類型并進(jìn)行條件推導(dǎo),實(shí)現(xiàn)中的關(guān)鍵點(diǎn)就是條件判斷邏輯,并使用 infer 提取到需要的類型進(jìn)行返回。
/** * Recursively unwraps the "awaited type" of a type. Non-promise "thenables" should resolve to `never`. This emulates the behavior of `await`. */ type Awaited<T> = T extends null | undefined ? T : // special case for `null | undefined` when not in `--strictNullChecks` mode T extends object & { then(onfulfilled: infer F, ...args: infer _): any } ? // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped F extends ((value: infer V, ...args: infer _) => any) ? // if the argument to `then` is callable, extracts the first argument Awaited<V> : // recursively unwrap the value never : // the argument to `then` was not callable T; // non-object or non-thenable
Partial<T> 、Required<T>、Readonly<T>
這三兄弟并不會導(dǎo)致T類型上的屬性數(shù)量變化,放在一起看。Partial<T> 將T類型中的所有屬性轉(zhuǎn)換成可選;Required<T> 則相反將T中可選屬性轉(zhuǎn)換成必要屬性。Readonly<T> 將T上所有的屬性都變成只可讀限制。
用法
type DataType = { id: number; origin?: string; } function mergeData(data: DataType, rest: Partial<DataType>): DataType { return { ...data, ...rest }; } function save(data: Required<DataType>) { // 寫庫需要完整的數(shù)據(jù), 包括 id 和 origin 來個(gè)字段 } const data: DataType = { id: 11 }; // ? const result = mergeData(data, {origin: 'DB-1'}); // ? save(data); /* Error Argument of type 'DataType' is not assignable to parameter of type 'Required<DataType>'. Types of property 'origin' are incompatible. Type 'string | undefined' is not assignable to type 'string'. Type 'undefined' is not assignable to type 'string' */
源碼實(shí)現(xiàn)以及實(shí)現(xiàn)原理,使用 keyof 讀取T類型中的所有屬性,然后從T中讀取對應(yīng)的類型進(jìn)行重新賦值。需要注意的是,此處不支持深層的處理,只處理了T的一級屬性。我們稍后自定義封裝一個(gè)能深度處理的工具類型。
// 源碼實(shí)現(xiàn) /** * Make all properties in T optional */ type Partial<T> = { [P in keyof T]?: T[P]; }; /** * Make all properties in T required */ type Required<T> = { [P in keyof T]-?: T[P]; }; /** * Make all properties in T readonly */ type Readonly<T> = { readonly [P in keyof T]: T[P]; };
ReturnType<T>、Parameters<T>、ConstructorParameters<T>
這三兄弟都是對函數(shù)進(jìn)行處理的,放在一起。ReturnType<T> 如果T是一個(gè)函數(shù)類型那么返回T的返回值類型,否則返回 never;Parameters<T> 如果T是一個(gè)函數(shù)類型,讀取其參數(shù)類型返回,非函數(shù)返回never,函參是個(gè)類數(shù)組實(shí)際返回的一個(gè) tuple 類型;ConstructorParameters<T> 讀取類上的構(gòu)造函數(shù)的函參列表類型,返回值類型和 Parameters<T> 一致。
function dataTransfer(a: number, b: string, c: Array<number>): boolean { return false; } type dataTransferReturnType = ReturnType<typeof dataTransfer> // 值類型 type dataTransferReturnType = boolean type dataTransferParametersType = Parameters<typeof dataTransfer> // 類型值 type dataTransferParametersType = [a: number, b: string, c: number[]] interface User { name: string; } class Student { private name:string; private age:number; constructor(name: string, age: number) { this.name = name; this.age = age; } } type StudentConstructorParametersType = ConstructorParameters<typeof Student> // 值類型: type StudentConstructorParametersType = [name: string, age: number]function dataTransfer(a: number, b: string, c: Array<number>): boolean { return false; } type dataTransferReturnType = ReturnType<typeof dataTransfer> // 值類型 type dataTransferReturnType = boolean type dataTransferParametersType = Parameters<typeof dataTransfer> // 類型值 type dataTransferParametersType = [a: number, b: string, c: number[]]
需要注意的是,這里使用了 typeof 獲取運(yùn)行時(shí)的變量值類型,也就是: typeof Student 提取了 Student 類的類型;使用 typeof dataTransfer 來提取了dataTransfer 函數(shù)的類型。typeof 忘了的往前翻翻,已備下了。
實(shí)現(xiàn)原理,借助條件類型以及 infer 的能力,提取參數(shù)類型、提取返回值類型。很簡單,看源碼吧。
// 源碼 type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never; /** * Obtain the parameters of a constructor function type in a tuple */ type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never; /** * Obtain the return type of a function type */ type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
Pick<T, Keys>、Record<Keys, T>
Pick 和 Record 是兩兄弟,大家可能有點(diǎn)疑問為什么不把Pick和Omit放在一起說【功能上Pick是得到交集,而Omit是選擇補(bǔ)集】,不放在一起說是因?yàn)閷?shí)現(xiàn)方式上 Omit 和 Exclude 更近一點(diǎn)。Pick<T, Keys> 表示從T中摘取屬性名屬于Keys中的類型集合; Record<Keys, T> 表示構(gòu)造一個(gè)新的類型,屬性名稱遍歷Keys,以T作為屬性類型。
interface Todo { title: string; description: string; completed: boolean; } type TodoPreview = Pick<Todo, "title" | "completed">; const todo: TodoPreview = { title: "Clean room", completed: false, }; //##__________________________________## interface CatInfo { age: number; breed: string; } type CatName = "miffy" | "boris" | "mordred"; const cats: Record<CatName, CatInfo> = { miffy: { age: 10, breed: "Persian" }, boris: { age: 5, breed: "Maine Coon" }, mordred: { age: 16, breed: "British Shorthair" }, };
需要注意的是,Pick<T,Keys> 以及 Record<Keys, T> 中的 keys 是聯(lián)合類型。
實(shí)現(xiàn)原理,讀取指定的Keys ,Pick從T中讀取對應(yīng)的類型,Record則完全使用傳入的T作為屬性類型。
// 源碼 /** * From T, pick a set of properties whose keys are in the union K */ type Pick<T, K extends keyof T> = { [P in K]: T[P]; }; /** * Construct a type with a set of properties K of type T */ type Record<K extends keyof any, T> = { [P in K]: T; };
Exclude<UnionType, ExcludedMembers>、Extract<Type, Union>
對立的兩兄弟,功能如下。
Exclude<UnionType, ExcludedMembers> 表示從UnionType 中排除可分配給 ExcludedMembers 里面的成員,返回剩下的類型。
Extract<Type, Union> 表示從Type中提取可以分配給Union并集的所有并集成員構(gòu)造一個(gè)新的類型。
Exclude<UnionType, ExcludedMembers> 用法
type T0 = Exclude<"a" | "b" | "c", "a">; //type T0 = "b" | "c" type T1 = Exclude<"a" | "b" | "c", "a" | "b">; //type T1 = "c" type T2 = Exclude<string | number | (() => void), Function>; // type T2 = string | number type Shape = | { kind: "circle"; radius: number } | { kind: "square"; x: number } | { kind: "triangle"; x: number; y: number }; type T3 = Exclude<Shape, { kind: "circle" }> // type T3 = | { kind: "square"; x: number } | { kind: "triangle"; x: number; y: number };
Extract<Type, Union> 用法
type T0 = Extract<"a" | "b" | "c", "a" | "f">; // ^? // type T0 = "a"; type T1 = Extract<string | number | (() => void), Function>; // ^? // T1 = ()=>void type Shape = | { kind: "circle"; radius: number } | { kind: "square"; x: number } | { kind: "triangle"; x: number; y: number }; type T2 = Extract<Shape, { kind: "circle" }> // ^? // type T2 = { kind: "circle"; radius: number } type T0 = Extract<"a" | "b" | "c", "a" | "f">; // ^? // type T0 = "a"; type T1 = Extract<string | number | (() => void), Function>; // ^? // T1 = ()=>void type Shape = | { kind: "circle"; radius: number } | { kind: "square"; x: number } | { kind: "triangle"; x: number; y: number }; type T2 = Extract<Shape, { kind: "circle" }> // ^? // type T2 = { kind: "circle"; radius: number }
實(shí)現(xiàn)原理,源碼庫里面看到的類型定義看起來比較簡單。
// 源碼 /** * Exclude from T those types that are assignable to U */ type Exclude<T, U> = T extends U ? never : T; /** * Extract from T those types that are assignable to U */ type Extract<T, U> = T extends U ? T : never;
可能大家會和我一樣一頭霧水,這一句怎么實(shí)現(xiàn)了聯(lián)合類型與聯(lián)合類型之間的計(jì)算呢?怎么也得有個(gè)遍歷的過程吧?(簡單說一下,TS 會針對聯(lián)合類型執(zhí)行對應(yīng)的遍歷過程,并且執(zhí)行=后面的條件類型判斷或操作,返回值會集中處理后返回給接受的類型變量(返回值可能是個(gè)聯(lián)合類型、也可能是一個(gè)普通類型,根據(jù)自定義工具代碼))。
我們簡單實(shí)現(xiàn)一個(gè)提取聯(lián)合類型中的指定屬性對應(yīng)的類型集合。
type Shape = |{ kind: "circle"; radius: number, x:string } | { kind: "square"; x: number } | { kind: "triangle"; x: boolean; y: number }; // 自定義一個(gè)工具類型 type PickOneKeyType<T, K extends string> = K extends keyof T ? ({ [P in K]: T[P] })[K] : never type XType = PickOneKeyType<Shape, 'x'> // type XType = string | number | boolean type KindType = PickOneKeyType<Shape, 'kind'> // type kindType = "circle" | "square" | "triangle"
【答案請大家關(guān)注下一篇文章:《TypeScript 玩轉(zhuǎn)類型下篇》】,我盡量研究清楚執(zhí)行過程及流程。
Omit<T, Keys>
Omit<T, Keys> 用于構(gòu)造一個(gè)新的類型屬性和屬性類型從T中讀取,刪除 Keys 中存在的屬性。有點(diǎn)像 Exclude 。Exclude 處理聯(lián)合類型,Omit 處理對象類型,屬性級別。
用法
type PointType = { x: number; y: number; getCoords: ()=> [number, number] } type DataType = Omit<PointType, 'getCoords'> /* type DataType = { x: number; y: number; }
Omit<T, K>實(shí)現(xiàn)源碼,先將篩選過濾得到目標(biāo)屬性集合,然后從T中讀取該屬性集合的類型。 使用到 Pick 和 Exclude 兩個(gè)類型工具。
// 源碼 /** * Construct a type with the properties of T except for those in type K. */ type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
總結(jié)
本文屬于 《TypeScript 玩轉(zhuǎn)類型操作》系列第一篇,可能大家好奇為什么沒有字符串方向的介紹?沒有自定義類型工具實(shí)戰(zhàn)?沒有TS體操面試題?
原因是TS對模板字符串的功能很強(qiáng)大內(nèi)容很多,所以將模板字符串+字符串處理工具類型放到一篇文章中,敬請關(guān)注。
類型工具實(shí)戰(zhàn),也會單開一篇文章也來寫。包含:深度 Partial、深度 Required 以及其他常用的類型工具定義。
以上就是TypeScript學(xué)習(xí)輕松玩轉(zhuǎn)類型操作的詳細(xì)內(nèi)容,更多關(guān)于TypeScript 類型操作的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
自動(dòng)生成typescript類型聲明工具實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了自動(dòng)生成typescript類型聲明工具實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04數(shù)據(jù)結(jié)構(gòu)TypeScript之鄰接表實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了數(shù)據(jù)結(jié)構(gòu)TypeScript之鄰接表實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01TypeScript 高級數(shù)據(jù)類型實(shí)例詳解
這篇文章主要為大家介紹了TypeScript 高級數(shù)據(jù)類型實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01TypeScript學(xué)習(xí)輕松玩轉(zhuǎn)類型操作
這篇文章主要為大家介紹了TypeScript學(xué)習(xí)輕松玩轉(zhuǎn)類型操作,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07FastAdmin表單驗(yàn)證data-rule插件—Nice-validator的使用方法
FastAdmin的表單驗(yàn)證data-rule非常方便,也很炫酷,采用的Nice-validator是一款非常強(qiáng)大的表單驗(yàn)證插件,通過簡單在元素上配置規(guī)則,即可達(dá)到驗(yàn)證的效果,怎么使用Nice-validator插件呢2023-09-09數(shù)據(jù)結(jié)構(gòu)TypeScript之鏈表實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了數(shù)據(jù)結(jié)構(gòu)TypeScript之鏈表實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01