日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

TypeScript中的keyof、typeof、索引訪問類型、條件類型

作者:jieyucx 更新時間: 2023-07-26 編程語言

一、keyof類型操作符

TypeScript中的keyof類型操作符可以獲取某個類型的所有屬性名組成的聯合類型。這個操作符的作用是幫助開發者在靜態類型檢查中更準確地操作屬性名。

舉例來說,如果我們有如下一個接口:

interface Person {
  name: string;
  age: number;
  gender: 'male' | 'female';
}

我們可以使用keyof來獲取這個接口的屬性名聯合類型:

type PersonKeys = keyof Person;
// 等價于:
// type PersonKeys = 'name' | 'age' | 'gender'

有了屬性名聯合類型,我們可以在編寫代碼時更準確地操作屬性名。以下是一些使用keyof的實際應用:

1. 動態獲取對象的屬性值

function getProperty<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

const person: Person = { name: 'Lucy', age: 18, gender: 'female' };
const name = getProperty(person, 'name'); // 類型為string
const age = getProperty(person, 'age'); // 類型為number
const gender = getProperty(person, 'gender'); // 類型為'male' | 'female'

在這個例子中,getProperty函數的第一個參數是一個泛型類型的對象,第二個參數是對象的屬性名。由于我們使用了keyof,所以在編寫代碼時我們可以確定屬性名的類型,并且編譯器也可以在編譯時進行類型檢查,保證我們不會誤操作屬性名或者試圖訪問不存在的屬性。

2. 限制對象的屬性種類

function createUser<Keys extends keyof Person>(name: string, value: Person[Keys]): Person {
  const user: Person = { name, age: 0, gender: 'male' };
  user[key] = value; // 編譯器知道key是Person的屬性名之一,不會有任何錯誤
  return user;
}

const newUser = createUser('Tom', 'male'); // 類型為Person
const errorUser = createUser('Jack', 'unknown'); // 編譯錯誤

在這個例子中,createUser函數通過泛型限制了屬性名的類型,而屬性名的類型只能從Person的屬性名中取值。在函數內部,我們可以安全地使用key來訪問person對象的屬性,因為key的類型是Person的屬性名之一。如果我們試圖傳入一個不合法的屬性名,編譯器會及時提示錯誤。

3. 避免硬編碼屬性名

class User {
  constructor(private data: Person) {}

  get<K extends keyof Person>(key: K): Person[K] {
    return this.data[key];
  }
}

const user = new User({ name: 'Lucy', age: 18, gender: 'female' });
const name = user.get('name'); // 類型為string
const age = user.get('age'); // 類型為number
const gender = user.get('gender'); // 類型為'male' | 'female'

在這個例子中,User類接受一個Person對象作為構造函數的參數,同時提供了一個get方法來獲取屬性值。我們使用了泛型和keyof,這樣在代碼中就不需要硬編碼屬性名,避免了潛在的錯誤。泛型的約束可以幫助我們在編譯時確保只能傳入合法的屬性名。

二、typeof類型操作符

TypeScript中的typeof類型操作符可以用來獲取一個值的類型信息,它返回一個代表該值類型的字符串。typeof操作符不會運行代碼,只會在編譯時進行類型檢查。

使用typeof類型操作符的場景包括:

1. 類型檢查:可以用來檢查變量的類型。例如,可以使用typeof操作符來檢查變量是不是一個字符串。

let str = 'hello world';
if (typeof str === 'string') {
  console.log('str is a string');
}

2. 類型推斷:可以使用typeof來推斷函數返回值的類型。

function double(num: number): number {
  return num * 2;
}

let num = 10;
let numDouble = double(num); // numDouble的類型被推斷為number

if (typeof numDouble === 'number') {
  console.log('numDouble is a number');
}

3. 編寫工具函數:可以使用typeof來編寫工具函數,比如判斷一個值是不是一個數組。

function isArray(value: any): value is Array<any> {
  return typeof value === 'object' && value !== null && Array.isArray(value);
}

let arr = [1, 2, 3];
if (isArray(arr)) {
  console.log('arr is an array');
}

4. 簡化重復代碼:可以使用typeof來簡化重復代碼,比如初始化一個對象中的屬性值。

interface Person {
  name: string;
  age: number;
}

function createPerson(name: string, age: number): Person {
  return {
    name,
    age,
    id: typeof name === 'string' ? name.slice(0, 3).toUpperCase() + age.toString() : '',
  };
}

let person = createPerson('John', 30);
console.log(person.id); // JOH30

總之,typeof類型操作符在TypeScript中有很多用途,可以幫助開發者更好地編寫類型安全的代碼。

三、索引訪問類型

索引訪問類型在TypeScript中是一種用于獲取類型中屬性或元素的方式,它通過字符串或數字索引來訪問具有下標的類型。它通常用于動態訪問對象屬性、數組元素和元組元素等。下面我們從多個角度介紹索引訪問類型并舉例說明。

1. 動態訪問對象屬性

假設我們有一個Person對象類型,它有兩個屬性name和age。我們可以通過索引訪問類型來動態獲取對象的屬性值。

type Person = { name: string; age: number };

type Name = Person['name']; // string
type Age = Person['age']; // number

const person: Person = { name: 'John', age: 18 };

function getProperty(obj: Person, key: keyof Person) {
  return obj[key];
}

const name: string = getProperty(person, 'name'); // 'John'
const age: number = getProperty(person, 'age'); // 18

在上述代碼中,我們定義了一個Person類型,并使用Person[‘name’]和Person[‘age’]來獲取分別獲取name和age的類型。我們使用keyof Person類型指定getProperty方法中的key參數只能傳遞Person對象的屬性名。最后我們通過getProperty方法動態獲取person對象的屬性值。

2. 動態訪問數組元素

索引訪問類型也可以用于動態訪問數組元素。我們可以通過索引類型來獲取數組元素的類型,也可以通過keyof Array類型來獲取數組的索引類型。

const fruits = ['apple', 'banana', 'orange'];

type Fruit = typeof fruits[number]; // 'apple' | 'banana' | 'orange'
type Index = keyof typeof fruits; // number | "length" | "toString" | "toLocaleString" | "push" | "pop" | "concat" | "join" | "reverse" | "shift" | "slice" | "sort" | "splice" | "unshift" | "indexOf" | "lastIndexOf" | "every" | "some" | "forEach" | "map" | "filter" | "reduce" | "reduceRight" | "entries" | "forEach" | "keys" | "values"

上述代碼中,我們定義了一個數組fruits,并使用typeof fruits[number]和keyof typeof fruits分別獲取了它的元素類型和索引類型。

3. 動態訪問元組元素

元組是一種特殊的數組類型,其元素類型可以不同。我們可以通過索引訪問類型來動態訪問元組元素。

type Tuple = [string, number];

type First = Tuple[0]; // string
type Second = Tuple[1]; // number

const tuple: Tuple = ['hello', 123];

function getTupleElement<T extends ReadonlyArray<any>, U extends keyof T>(
  tuple: T,
  index: U,
): T[U] {
  return tuple[index];
}

const first: string = getTupleElement(tuple, 0); // 'hello'
const second: number = getTupleElement(tuple, 1); // 123

在上述代碼中,我們定義了一個元組類型Tuple,通過Tuple[0]和Tuple[1]分別獲取了它的第一個和第二個元素類型。同時,我們定義了一個getTupleElement方法,使用泛型T和U分別表示元組類型和索引類型,并通過T[U]獲取元組指定索引處的元素。

總之,索引訪問類型可以用于動態訪問對象屬性、數組元素和元組元素。它可以方便地處理動態類型,增強了TypeScript的靈活性和適用性。

四、條件類型

TypeScript中的條件類型可以根據某個類型的特定屬性或條件,選擇不同的類型。條件類型是TypeScript高級類型中的一種,可以用于定義泛型的約束條件,從而增強代碼的類型安全性和靈活性。

下面從不同角度舉例分析說明TypeScript中的條件類型:

1. 根據屬性判斷是否可選

條件類型可以根據某個屬性是否存在或者是否可選來確定不同的類型。例如,以下代碼中,當T中的K屬性為可選屬性時,返回Partial類型;當K為必選屬性時,返回T本身:

type MyType<T, K extends keyof T> = K extends keyof T ? Partial<T> : T;

2. 根據屬性值判斷是否滿足條件

條件類型可以根據某個屬性的值是否滿足條件來確定不同的類型。例如,以下代碼中,當T中的K屬性的類型為U時,返回T本身;否則返回never類型:

type MyType<T, K extends keyof T, U> = T[K] extends U ? T : never;

3. 根據類型之間的關系判斷是否滿足條件

條件類型可以根據不同類型之間的關系來確定不同的類型。例如,以下代碼中,當T為U的子類型時,返回T本身;否則返回never類型:

type MyType<T, U> = T extends U ? T : never;

4. 根據函數參數類型判斷返回值類型

條件類型可以根據函數參數的類型來確定函數返回值類型。例如,以下代碼中,當T為函數類型時,返回函數返回值的類型;否則返回never類型:

type MyType<T> = T extends (...args: any[]) => infer R ? R : never;

5. 根據對象類型判斷是否有特定屬性

條件類型可以根據對象類型中是否含有特定屬性來確定不同的類型。例如,以下代碼中,當T中含有名為K的屬性時,返回T本身;否則返回never類型:

type MyType<T, K> = keyof T extends K ? T : never;

通過以上幾個例子,可以看出條件類型在TypeScript中的靈活性和強大的約束能力。條件類型可以根據不同的情況進行不同的判斷,從而增強代碼的可讀性和可維護性。

五、類型推理infer

在TypeScript中,類型推理是一種自動推斷變量類型的機制,它可以根據變量的使用上下文以及其值的類型來推斷變量的類型。而infer關鍵字是TypeScript的一種高級類型操作符它可以用來從已知類型中推斷出未知類型,讓類型推理更加靈活

infer關鍵字通常在條件類型中使用,其中條件類型可以根據條件來決定返回的類型。infer用于捕獲條件類型中的未知類型,然后可以用該類型來進行操作。下面分別從多角度舉例說明infer如何使用。

1. 從函數參數中推斷類型

在下面的這個例子中,我們可以看到如何使用infer關鍵字從函數參數中推斷出其類型:

type ParameterType<T extends (...args: any) => any> = T extends ((arg: infer P) => any) ? P : never;

function foo(param: string) {}

type ParamType = ParameterType<typeof foo>; // string

在這個例子中,我們定義了一個ParameterType類型,它接受一個函數類型作為參數。然后,我們使用infer關鍵字來推斷函數類型的參數類型,并將此類型指定為類型別名ParamType的值。

2. 從數組或元組中推斷類型

在下面的這個例子中,我們可以看到如何使用infer關鍵字從數組或元組中推斷出其類型:

type ArrayType<T> = T extends Array<infer U> ? U : never;
type TupleType<T> = T extends [infer U, ...infer V] ? [U, ...V] : never;

type A = ArrayType<number[]>; // number
type B = TupleType<[string, number, boolean]>; // [string, number, boolean]

在這個例子中,我們定義了兩個類型別名ArrayType和TupleType。ArrayType接受一個數組類型作為參數,使用infer關鍵字推斷出數組元素的類型,并將其作為其返回類型。TupleType接受一個元組類型作為參數,使用infer關鍵字推斷出元組中第一個元素的類型,并使用剩余類型推斷出元組剩余的元素類型。然后,我們分別將這些類型應用于變量A和變量B,得到相應的類型結果。

3. 從Promise中推斷類型

在下面的這個例子中,我們可以看到如何使用infer關鍵字從Promise中推斷出其類型:

type PromiseType<T> = T extends Promise<infer U> ? U : never;

async function foo(): Promise<string> {
  return 'hello';
}

type FooType = PromiseType<ReturnType<typeof foo>>; // string

在這個例子中,我們定義了一個PromiseType類型,它接受一個Promise類型作為參數,使用infer關鍵字推斷出Promise的值類型,并將其作為其返回類型。然后,我們定義了一個異步函數foo,其返回類型為Promise。最后,我們將函數foo的ReturnType應用于PromiseType類型,并得到其返回值類型為string的結果。

總之,infer是TypeScript中一個非常重要的高級類型操作符,可以用于從已知類型中推斷出未知類型,讓類型推理更加靈活。通過上述多個角度的示例,希望可以更好地理解infer在TypeScript中的實際應用。

六、分布式條件類型

分布式條件類型是TypeScript的一項高級特性,它也是條件類型的一種。分布式條件類型可以根據一個類型參數 T,在聯合類型中判斷 T 是否為其他類型,并根據 T 是該類型或其子集來生成新類型。這個生成過程會在聯合類型中遍歷每一個元素,并生成對應的類型。與普通的條件類型不同的是,分布式條件類型會把聯合類型的操作分發到每一個元素中。

下面我們通過舉例分析,更全面地了解分布式條件類型。

1、 基本使用

首先,我們看一個最基本的例子:

type IfNumber<T> = T extends number ? 'yes' : 'no';
type A = IfNumber<1>; // 'yes'
type B = IfNumber<'a'>; // 'no'
type C = IfNumber<number | string>; // 'yes' | 'no'

這里,我們定義了一個條件類型IfNumber,當T是number類型的時候返回’yes’,否則返回’no’。當我們分別傳入1、‘a’、number | string三種類型作為類型參數進行測試時,分別返回’yes’、‘no’、‘yes’ | ‘no’。

2、 分布式條件類型在泛型中的應用

分布式條件類型可以用在泛型中,作為泛型約束的一部分。比如,我們可以定義一個函數,用于獲取對象的屬性值。如果對象的屬性值是數字,返回數字類型的對象,否則返回字符串類型的對象。

type ObjectValue<T> = T extends { [key: string]: infer U } ? U : never;
type ObjectWithType<T, U> = { [K in keyof T]: ObjectValue<T[K]> extends U ? T[K] : never };
type ExtractObject<O, U> = ObjectWithType<O, U>[keyof O];

function getPropByType<O, U>(obj: O, type: U): ExtractObject<O, U>[] {
  const result = [];
  for (const key in obj) {
    const value = obj[key];
    if (typeof value === typeof type) {
      result.push(value);
    }
  }
  return result;
}

const obj = {
  a: 1,
  b: '2',
  c: 3,
};
const result = getPropByType(obj, '2');

這里,我們使用分布式條件類型ObjectWithType來定義一個新類型ObjectWithType<T, U>,它可以將T中每個屬性值的類型做比較,只有當屬性值的類型===U時才保留該屬性。同時,我們使用ObjectValue來輔助獲取對象屬性值的類型。在getPropByType函數中,我們傳入一個對象和一個類型參數U,函數會遍歷對象的屬性值,檢查它們的類型是否等于U,并將符合條件的屬性值保存在數組中后返回。

3、 分布式條件類型在類型映射中的應用

分布式條件類型還可以用于類型映射中。下面我們使用分布式條件類型,實現一個將對象中所有屬性變成可選屬性的函數。

type Optionalize<T> =
  T extends any ? {
    [K in keyof T]?: Optionalize<T[K]>;
  } : never;

function optionalize<Key extends string, O extends { [K in Key]: any }>(obj: O): Optionalize<O> {
  const result: Optionalize<O> = {};
  for (const key in obj) {
    const value = obj[key];
    if (typeof value === 'object' && !Array.isArray(value)) {
      result[key] = optionalize(value);
    } else {
      result[key as keyof O] = value;
    }
  }
  return result;
}

const obj = {
  a: {
    b: 1,
    c: {
      d: 2,
    },
  },
  e: '3',
};
const optionalObj = optionalize(obj);

這里,我們使用了分布式條件類型來定義Optionalize類型。它首先認為T可以是任何類型,然后對于T中的每個屬性K,我們都將它變成一個可選屬性。在optionalize函數中,我們分別遍歷了obj的所有屬性,并根據value的類型決定是遞歸處理還是復制value的值到result中,最終返回了一個可選屬性合集Optionalize。

分布式條件類型是一項高級特性,它可以對聯合類型的所有元素進行操作,生成多樣化的新類型。我們在實際場景中可以通過它來解決一些復雜的問題,比如提取對象中某個類型的屬性、將對象中的屬性轉換成可選屬性。掌握這個知識點,可以讓我們更好地應對類型轉換的挑戰。

原文鏈接:https://blog.csdn.net/jieyucx/article/details/131363515

  • 上一篇:沒有了
  • 下一篇:沒有了
欄目分類
最近更新