十個你必須要會的TypeScript技巧分享
1. 泛型的使用
泛型可以讓我們編寫更具靈活性、可重用性和類型安全性的代碼。在 TypeScript 中,泛型通常使用類型參數(shù)來定義一個通用的類型或函數(shù),并在使用時指定具體的類型。
我們想編寫一個函數(shù)來反轉(zhuǎn)任意數(shù)組,假設我們不使用泛型,代碼可能是這樣↓
function reverseStrings(items: string[]): string[] {
return items.reverse();
}
function reverseNumbers(items: number[]): number[] {
return items.reverse();
}但是這種方法顯然不夠優(yōu)雅,因為我們需要分別編寫兩個函數(shù)來處理 string 和 number 類型的數(shù)組,并且當我們需要處理其他類型的數(shù)組時,我們必須再次編寫新的函數(shù)。
使用泛型,我們可以很容易地創(chuàng)建一個通用的函數(shù)來處理任何類型的數(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. 利用交叉類型和聯(lián)合類型
交叉類型(Intersection Types)允許將多個類型合并為一個類型,新類型將具有所有類型的特性。我們可以使用符號 & 運算符將兩個或多個類型組合成一個交叉類型。
聯(lián)合類型(Union Types)表示一個值可以有多種類型之一。我們可以使用符號 | 運算符將兩個或多個類型組合成一個聯(lián)合類型。
// 交叉類型
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)合類型的使用也非常簡單,用法就是使用 | 運算符將多個類型組合在一起
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. 類型推斷
類型推斷(Type Inference)是 TypeScript 的一個強大的特性。它允許編譯器根據(jù)上下文自動推斷出變量的類型,從而減少手動輸入類型的工作量,同時也提高了代碼的可維護性和可讀性。
栗子
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 中的一個關鍵字,用于獲取對象類型的所有鍵的聯(lián)合類型。它可以幫助我們在編寫泛型函數(shù)或操作對象屬性時,提供更好的類型安全性。
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. 映射類型
TypeScript 中的映射類型(Mapped Types)是一種非常強大的類型操作符,它可以根據(jù)一個已有的對象類型,生成一個新的對象類型。映射類型可以幫助我們進行一些常見的類型轉(zhuǎn)換和操作,如將所有屬性變成可選屬性、添加或刪除屬性、修改屬性類型等等。
TypeScript 中的映射類型有以下四種:
1.Partial<T>:將類型 T 中所有屬性變?yōu)榭蛇x屬性。
interface Person {
name: string;
age: number;
gender: 'male' | 'female';
}
type PartialPerson = Partial<Person>;
// 等價于
// interface PartialPerson {
// name?: string;
// age?: number;
// gender?: 'male' | 'female';
// }2.Readonly<T>:將類型 T 中所有屬性變?yōu)橹蛔x屬性。
interface Person {
name: string;
age: number;
gender: 'male' | 'female';
}
type ReadonlyPerson = Readonly<Person>;
// 等價于
// interface ReadonlyPerson {
// readonly name: string;
// readonly age: number;
// readonly gender: 'male' | 'female';
// }3.Record<K, T>:創(chuàng)建一個新的對象類型,其屬性名類型為 K,屬性值類型為 T。
type Dictionary<T> = Record<string, T>;
let dict: Dictionary<number> = {
foo: 123,
bar: 456,
};4.Pick<T, K>:從類型 T 中選擇指定的屬性 K,并返回一個新的對象類型。
interface Person {
name: string;
age: number;
gender: 'male' | 'female';
}
type PersonNameAndAge = Pick<Person, 'name' | 'age'>;
// 等價于
// interface PersonNameAndAge {
// name: string;
// age: number;
// }還有一種映射類型叫做 Keyof,它用于獲取一個對象類型中所有屬性名組成的聯(lián)合類型。這個類型在前面的問題中已經(jīng)講到過了,這里就不再贅述。
6. 類型別名和接口
1. 類型別名
類型別名(Type Aliases)是一種給一個已經(jīng)存在的類型起一個新的名字的方式。通過 type 關鍵字可以定義一個類型別名。
type MyString = string;
type MyNumber = number;
type Person = {
name: string;
age: number;
};類型別名可以很方便地給復雜的類型定義一個簡潔的名稱,從而提高代碼可讀性,并且還可以使用聯(lián)合類型、交叉類型等高級類型
type Color = 'red' | 'green' | 'blue';
type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; length: number };
function draw(shape: Shape, color: Color) {
// ...
}2.接口
接口(Interfaces)是一種描述對象結(jié)構(gòu)的方式,在 TypeScript 中通過 interface 關鍵字來定義。接口可以包含屬性、方法和索引簽名等
interface Person {
name: string;
age: number;
sayHello: () => void;
}
let person: Person = {
name: 'Alice',
age: 30,
sayHello() {
console.log(`Hello, my name is ${this.name}.`);
},
};接口在描述對象結(jié)構(gòu)時非常有用,它可以提供更好的代碼組織性和可讀性,并且也可以在一些特定場景下提供更好的類型安全性。另外需要注意的是,接口只能描述對象的形狀,不能描述具體的實現(xiàn)方式。如果需要描述具體的實現(xiàn)方式,可以使用類或函數(shù)類型。
7. 裝飾器
裝飾器是一種特殊的語法,它可以用來修飾類、方法、屬性以及參數(shù)等元素,從而達到一些特定的目的。在 TypeScript 中,我們可以使用 @ 符號來聲明一個裝飾器
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 中的裝飾器可以用于很多場景,例如實現(xiàn)依賴注入、自動綁定事件、路由映射等等。常見的裝飾器包括 @Injectable、@Component、@ViewChild、@RouterConfig 等等。
8. 類型守衛(wèi)
類型守衛(wèi)(Type Guards)是 TypeScript 中用來檢測類型的一種機制,它可以幫助開發(fā)者在運行時檢測某個變量的類型,并在不同的條件下提供不同的類型聲明。
在 TypeScript 中,有四種常見的類型守衛(wèi)方式:
1.typeof 類型守衛(wèi)
function foo(x: number | string) {
if (typeof x === 'number') {
// x is number
} else {
// x is string
}
}2.instanceof 類型守衛(wèi)
class MyClass {}
function foo(x: any) {
if (x instanceof MyClass) {
// x is an instance of MyClass
}
}3.自定義類型守衛(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 操作符類型守衛(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)是一種特殊的類型文件,用來描述外部 JavaScript 庫、模塊或?qū)ο蟮念愋?,以便?TypeScript 代碼中正確引用和使用它們。
TypeScript 編譯器可以根據(jù) JavaScript 庫的源代碼推斷出其類型信息,但某些 JavaScript 庫并沒有提供類型定義文件,或者類型定義文件不完整或不準確,這時我們需要手動編寫聲明文件。聲明文件的擴展名為 .d.ts,可以與 TypeScript 文件一起放置在項目目錄中。聲明文件的編寫方式有以下幾種:
1.定義全局變量和函數(shù)
如果我們需要在 TypeScript 代碼中調(diào)用瀏覽器原生 API 或其他 JavaScript 庫中的全局變量和函數(shù),就需要手動編寫聲明文件來告訴 TypeScript 對應變量和函數(shù)的類型。例如:
declare const $: (selector: string) => any;
$('#my-element').addClass('highlight');2.擴展已有類型
有時候我們需要擴展已有的類型定義,以適應自己的需求,這時可以使用 interface、namespace 等關鍵字來定義和擴展類型。例如:
interface String {
reverse(): string;
}
const str = 'Hello, world!';
console.log(str.reverse()); // "!dlrow ,olleH"3.模塊聲明
如果我們要使用一個已有的 JavaScript 模塊,但模塊本身沒有提供類型定義文件,或者類型定義文件不完整或不準確,這時我們需要手動編寫聲明文件來告訴 TypeScript 模塊的類型信息。例如:
declare module 'my-module' {
export function greet(name: string): string;
}10. 類型化事件
類型化事件(Typed Event)是一種可以指定事件處理函數(shù)接收參數(shù)類型、返回值類型的事件機制。通過使用類型化事件,我們可以在編譯時對事件處理函數(shù)的類型進行檢查,以避免運行時因類型不匹配而導致的錯誤。
舉個栗子,如何定義和使用類型化事件↓
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);
}
}
}
// 定義一個事件參數(shù)類型
interface MyEventArgs {
message: string;
}
// 創(chuàng)建一個類型化事件實例
const myEvent = new TypedEvent<MyEventArgs>();
// 添加一個事件處理函數(shù)
myEvent.addHandler((args: MyEventArgs) => {
console.log(args.message);
});
// 觸發(fā)事件
myEvent.raise({ message: 'Hello, world!' });以上就是十個你必須要會的TypeScript技巧分享的詳細內(nèi)容,更多關于TypeScript技巧的資料請關注腳本之家其它相關文章!
相關文章
Bootstrap Table表格一直加載(load)不了數(shù)據(jù)的快速解決方法
bootstrap-table是一個基于Bootstrap風格的強大的表格插件神器。接下來通過本文給大家介紹Bootstrap Table表格一直加載(load)不了數(shù)據(jù)的快速解決方法,感興趣的朋友一起看看吧2016-09-09
Js數(shù)組的操作push,pop,shift,unshift等方法詳細介紹
js中針對數(shù)組操作的方法還是比較多的,今天突然想到來總結(jié)一下,也算是溫故而知新吧。不過不會針對每個方法進行講解,我只是選擇其中的一些來講,感興趣的朋友可以研究一下2012-12-12

