十個(gè)你必須要會(huì)的TypeScript技巧分享
1. 泛型的使用
泛型可以讓我們編寫(xiě)更具靈活性、可重用性和類(lèi)型安全性的代碼。在 TypeScript 中,泛型通常使用類(lèi)型參數(shù)來(lái)定義一個(gè)通用的類(lèi)型或函數(shù),并在使用時(shí)指定具體的類(lèi)型。
我們想編寫(xiě)一個(gè)函數(shù)來(lái)反轉(zhuǎn)任意數(shù)組,假設(shè)我們不使用泛型,代碼可能是這樣↓
function reverseStrings(items: string[]): string[] { return items.reverse(); } function reverseNumbers(items: number[]): number[] { return items.reverse(); }
但是這種方法顯然不夠優(yōu)雅,因?yàn)槲覀冃枰謩e編寫(xiě)兩個(gè)函數(shù)來(lái)處理 string 和 number 類(lèi)型的數(shù)組,并且當(dāng)我們需要處理其他類(lèi)型的數(shù)組時(shí),我們必須再次編寫(xiě)新的函數(shù)。
使用泛型,我們可以很容易地創(chuàng)建一個(gè)通用的函數(shù)來(lái)處理任何類(lèi)型的數(shù)組:
function reverse<T>(items: T[]): T[] { return items.reverse(); } const words = ['hello', 'world']; const reversedWords = reverse<string>(words); console.log(reversedWords); // ['world', 'hello'] const numbers = [1, 2, 3]; const reversedNumbers = reverse<number>(numbers); console.log(reversedNumbers); // [3, 2, 1]
2. 利用交叉類(lèi)型和聯(lián)合類(lèi)型
交叉類(lèi)型(Intersection Types)允許將多個(gè)類(lèi)型合并為一個(gè)類(lèi)型,新類(lèi)型將具有所有類(lèi)型的特性。我們可以使用符號(hào) & 運(yùn)算符將兩個(gè)或多個(gè)類(lèi)型組合成一個(gè)交叉類(lèi)型。
聯(lián)合類(lèi)型(Union Types)表示一個(gè)值可以有多種類(lèi)型之一。我們可以使用符號(hào) | 運(yùn)算符將兩個(gè)或多個(gè)類(lèi)型組合成一個(gè)聯(lián)合類(lèi)型。
// 交叉類(lèi)型 interface Dog { walk(): void; } interface Cat { meow(): void; } type Pet = Dog & Cat; const myPet: Pet = { walk() { console.log('walking') }, meow() { console.log('meowing') } }
聯(lián)合類(lèi)型的使用也非常簡(jiǎn)單,用法就是使用 | 運(yùn)算符將多個(gè)類(lèi)型組合在一起
interface Square { side: number; } interface Circle { radius: number; } function calculateArea(shape: Square | Circle) { if ('side' in shape) { return shape.side ** 2; } else { return Math.PI * shape.radius ** 2; } }
3. 類(lèi)型推斷
類(lèi)型推斷(Type Inference)是 TypeScript 的一個(gè)強(qiáng)大的特性。它允許編譯器根據(jù)上下文自動(dòng)推斷出變量的類(lèi)型,從而減少手動(dòng)輸入類(lèi)型的工作量,同時(shí)也提高了代碼的可維護(hù)性和可讀性。
栗子
let num = 5; let str = "hello"; let bool = true; function add(a: number, b: number) { return a + b; } let result = add(num, 10);
4. keyof
keyof 是 TypeScript 中的一個(gè)關(guān)鍵字,用于獲取對(duì)象類(lèi)型的所有鍵的聯(lián)合類(lèi)型。它可以幫助我們?cè)诰帉?xiě)泛型函數(shù)或操作對(duì)象屬性時(shí),提供更好的類(lèi)型安全性。
interface Person { name: string; age: number; gender: 'male' | 'female'; } function getProperty<T, K extends keyof T>(obj: T, key: K) { return obj[key]; } let person: Person = { name: 'Alice', age: 30, gender: 'female' }; let name = getProperty(person, 'name'); let age = getProperty(person, 'age'); let gender = getProperty(person, 'gender');
5. 映射類(lèi)型
TypeScript 中的映射類(lèi)型(Mapped Types)是一種非常強(qiáng)大的類(lèi)型操作符,它可以根據(jù)一個(gè)已有的對(duì)象類(lèi)型,生成一個(gè)新的對(duì)象類(lèi)型。映射類(lèi)型可以幫助我們進(jìn)行一些常見(jiàn)的類(lèi)型轉(zhuǎn)換和操作,如將所有屬性變成可選屬性、添加或刪除屬性、修改屬性類(lèi)型等等。
TypeScript 中的映射類(lèi)型有以下四種:
1.Partial<T>:將類(lèi)型 T 中所有屬性變?yōu)榭蛇x屬性。
interface Person { name: string; age: number; gender: 'male' | 'female'; } type PartialPerson = Partial<Person>; // 等價(jià)于 // interface PartialPerson { // name?: string; // age?: number; // gender?: 'male' | 'female'; // }
2.Readonly<T>:將類(lèi)型 T 中所有屬性變?yōu)橹蛔x屬性。
interface Person { name: string; age: number; gender: 'male' | 'female'; } type ReadonlyPerson = Readonly<Person>; // 等價(jià)于 // interface ReadonlyPerson { // readonly name: string; // readonly age: number; // readonly gender: 'male' | 'female'; // }
3.Record<K, T>:創(chuàng)建一個(gè)新的對(duì)象類(lèi)型,其屬性名類(lèi)型為 K,屬性值類(lèi)型為 T。
type Dictionary<T> = Record<string, T>; let dict: Dictionary<number> = { foo: 123, bar: 456, };
4.Pick<T, K>:從類(lèi)型 T 中選擇指定的屬性 K,并返回一個(gè)新的對(duì)象類(lèi)型。
interface Person { name: string; age: number; gender: 'male' | 'female'; } type PersonNameAndAge = Pick<Person, 'name' | 'age'>; // 等價(jià)于 // interface PersonNameAndAge { // name: string; // age: number; // }
還有一種映射類(lèi)型叫做 Keyof,它用于獲取一個(gè)對(duì)象類(lèi)型中所有屬性名組成的聯(lián)合類(lèi)型。這個(gè)類(lèi)型在前面的問(wèn)題中已經(jīng)講到過(guò)了,這里就不再贅述。
6. 類(lèi)型別名和接口
1. 類(lèi)型別名
類(lèi)型別名(Type Aliases)是一種給一個(gè)已經(jīng)存在的類(lèi)型起一個(gè)新的名字的方式。通過(guò) type 關(guān)鍵字可以定義一個(gè)類(lèi)型別名。
type MyString = string; type MyNumber = number; type Person = { name: string; age: number; };
類(lèi)型別名可以很方便地給復(fù)雜的類(lèi)型定義一個(gè)簡(jiǎn)潔的名稱(chēng),從而提高代碼可讀性,并且還可以使用聯(lián)合類(lèi)型、交叉類(lèi)型等高級(jí)類(lèi)型
type Color = 'red' | 'green' | 'blue'; type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; length: number }; function draw(shape: Shape, color: Color) { // ... }
2.接口
接口(Interfaces)是一種描述對(duì)象結(jié)構(gòu)的方式,在 TypeScript 中通過(guò) interface 關(guān)鍵字來(lái)定義。接口可以包含屬性、方法和索引簽名等
interface Person { name: string; age: number; sayHello: () => void; } let person: Person = { name: 'Alice', age: 30, sayHello() { console.log(`Hello, my name is ${this.name}.`); }, };
接口在描述對(duì)象結(jié)構(gòu)時(shí)非常有用,它可以提供更好的代碼組織性和可讀性,并且也可以在一些特定場(chǎng)景下提供更好的類(lèi)型安全性。另外需要注意的是,接口只能描述對(duì)象的形狀,不能描述具體的實(shí)現(xiàn)方式。如果需要描述具體的實(shí)現(xiàn)方式,可以使用類(lèi)或函數(shù)類(lèi)型。
7. 裝飾器
裝飾器是一種特殊的語(yǔ)法,它可以用來(lái)修飾類(lèi)、方法、屬性以及參數(shù)等元素,從而達(dá)到一些特定的目的。在 TypeScript 中,我們可以使用 @ 符號(hào)來(lái)聲明一個(gè)裝飾器
function log(target: any, key: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; descriptor.value = function(...args: any[]) { console.log(`Call ${key} with args: ${JSON.stringify(args)}`); return originalMethod.apply(this, args); }; return descriptor; } class MyClass { @log greet(name: string) { console.log(`Hello, ${name}!`); } }
TypeScript 中的裝飾器可以用于很多場(chǎng)景,例如實(shí)現(xiàn)依賴(lài)注入、自動(dòng)綁定事件、路由映射等等。常見(jiàn)的裝飾器包括 @Injectable、@Component、@ViewChild、@RouterConfig 等等。
8. 類(lèi)型守衛(wèi)
類(lèi)型守衛(wèi)(Type Guards)是 TypeScript 中用來(lái)檢測(cè)類(lèi)型的一種機(jī)制,它可以幫助開(kāi)發(fā)者在運(yùn)行時(shí)檢測(cè)某個(gè)變量的類(lèi)型,并在不同的條件下提供不同的類(lèi)型聲明。
在 TypeScript 中,有四種常見(jiàn)的類(lèi)型守衛(wèi)方式:
1.typeof 類(lèi)型守衛(wèi)
function foo(x: number | string) { if (typeof x === 'number') { // x is number } else { // x is string } }
2.instanceof 類(lèi)型守衛(wèi)
class MyClass {} function foo(x: any) { if (x instanceof MyClass) { // x is an instance of MyClass } }
3.自定義類(lèi)型守衛(wèi)函數(shù)
interface A { a: number } interface B { b: number } function isA(x: any): x is A { return typeof x.a === 'number'; } function foo(x: A | B) { if (isA(x)) { // x is an instance of A } else { // x is an instance of B } }
4.in 操作符類(lèi)型守衛(wèi)
interface A { a: number } interface B { b: number } function foo(x: A | B) { if ('a' in x) { // x is an instance of A } else { // x is an instance of B } }
9. 聲明文件
聲明文件(Declaration File)是一種特殊的類(lèi)型文件,用來(lái)描述外部 JavaScript 庫(kù)、模塊或?qū)ο蟮念?lèi)型,以便在 TypeScript 代碼中正確引用和使用它們。
TypeScript 編譯器可以根據(jù) JavaScript 庫(kù)的源代碼推斷出其類(lèi)型信息,但某些 JavaScript 庫(kù)并沒(méi)有提供類(lèi)型定義文件,或者類(lèi)型定義文件不完整或不準(zhǔn)確,這時(shí)我們需要手動(dòng)編寫(xiě)聲明文件。聲明文件的擴(kuò)展名為 .d.ts,可以與 TypeScript 文件一起放置在項(xiàng)目目錄中。聲明文件的編寫(xiě)方式有以下幾種:
1.定義全局變量和函數(shù)
如果我們需要在 TypeScript 代碼中調(diào)用瀏覽器原生 API 或其他 JavaScript 庫(kù)中的全局變量和函數(shù),就需要手動(dòng)編寫(xiě)聲明文件來(lái)告訴 TypeScript 對(duì)應(yīng)變量和函數(shù)的類(lèi)型。例如:
declare const $: (selector: string) => any; $('#my-element').addClass('highlight');
2.擴(kuò)展已有類(lèi)型
有時(shí)候我們需要擴(kuò)展已有的類(lèi)型定義,以適應(yīng)自己的需求,這時(shí)可以使用 interface、namespace 等關(guān)鍵字來(lái)定義和擴(kuò)展類(lèi)型。例如:
interface String { reverse(): string; } const str = 'Hello, world!'; console.log(str.reverse()); // "!dlrow ,olleH"
3.模塊聲明
如果我們要使用一個(gè)已有的 JavaScript 模塊,但模塊本身沒(méi)有提供類(lèi)型定義文件,或者類(lèi)型定義文件不完整或不準(zhǔn)確,這時(shí)我們需要手動(dòng)編寫(xiě)聲明文件來(lái)告訴 TypeScript 模塊的類(lèi)型信息。例如:
declare module 'my-module' { export function greet(name: string): string; }
10. 類(lèi)型化事件
類(lèi)型化事件(Typed Event)是一種可以指定事件處理函數(shù)接收參數(shù)類(lèi)型、返回值類(lèi)型的事件機(jī)制。通過(guò)使用類(lèi)型化事件,我們可以在編譯時(shí)對(duì)事件處理函數(shù)的類(lèi)型進(jìn)行檢查,以避免運(yùn)行時(shí)因類(lèi)型不匹配而導(dǎo)致的錯(cuò)誤。
舉個(gè)栗子,如何定義和使用類(lèi)型化事件↓
interface EventHandler<T> { (args: T): void; } class TypedEvent<T> { private handlers: EventHandler<T>[] = []; public addHandler(handler: EventHandler<T>) { this.handlers.push(handler); } public removeHandler(handler: EventHandler<T>) { const index = this.handlers.indexOf(handler); if (index >= 0) { this.handlers.splice(index, 1); } } public raise(args: T) { for (const handler of this.handlers) { handler(args); } } } // 定義一個(gè)事件參數(shù)類(lèi)型 interface MyEventArgs { message: string; } // 創(chuàng)建一個(gè)類(lèi)型化事件實(shí)例 const myEvent = new TypedEvent<MyEventArgs>(); // 添加一個(gè)事件處理函數(shù) myEvent.addHandler((args: MyEventArgs) => { console.log(args.message); }); // 觸發(fā)事件 myEvent.raise({ message: 'Hello, world!' });
以上就是十個(gè)你必須要會(huì)的TypeScript技巧分享的詳細(xì)內(nèi)容,更多關(guān)于TypeScript技巧的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
layerui代碼控制tab選項(xiàng)卡,添加,關(guān)閉的實(shí)例
今天小編就為大家分享一篇layerui代碼控制tab選項(xiàng)卡,添加,關(guān)閉的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-09-09JavaScript 精美貪吃蛇實(shí)現(xiàn)流程
看起來(lái)好像很復(fù)雜的貪吃蛇,到底是怎么用JavaScript去實(shí)現(xiàn)的?下面就來(lái)一步一步地,剖析怎么用JavaScript,放在任意一個(gè)瀏覽器中,把貪吃蛇搞起來(lái)2021-11-11Bootstrap Table表格一直加載(load)不了數(shù)據(jù)的快速解決方法
bootstrap-table是一個(gè)基于Bootstrap風(fēng)格的強(qiáng)大的表格插件神器。接下來(lái)通過(guò)本文給大家介紹Bootstrap Table表格一直加載(load)不了數(shù)據(jù)的快速解決方法,感興趣的朋友一起看看吧2016-09-09原生JS實(shí)現(xiàn)《別踩白塊》游戲(兼容IE)
本文主要介紹了原生JS實(shí)現(xiàn)《別踩白塊》游戲(兼容IE)的示例代碼。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-02-02JS獲取圖片實(shí)際寬高及根據(jù)圖片大小進(jìn)行自適應(yīng)
圖片實(shí)際寬高使用js進(jìn)行獲取以及根據(jù)圖片大小進(jìn)行自適應(yīng),此功能個(gè)人感覺(jué)比較實(shí)用,在此貢獻(xiàn)出來(lái),希望對(duì)大家有所幫助2013-08-08Js數(shù)組的操作push,pop,shift,unshift等方法詳細(xì)介紹
js中針對(duì)數(shù)組操作的方法還是比較多的,今天突然想到來(lái)總結(jié)一下,也算是溫故而知新吧。不過(guò)不會(huì)針對(duì)每個(gè)方法進(jìn)行講解,我只是選擇其中的一些來(lái)講,感興趣的朋友可以研究一下2012-12-12JS+CSS實(shí)現(xiàn)下拉刷新/上拉加載插件
這篇文章主要介紹了JS+CSS實(shí)現(xiàn)下拉刷新/上拉加載插件,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-03-03Js-$.extend擴(kuò)展方法使方法參數(shù)更靈活
在JS里,我們的方法參數(shù)通常使用JQ的$.extend擴(kuò)展方法來(lái)實(shí)現(xiàn),感興趣的朋友可以了解下2013-01-01