最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

TypeScript?類型收窄的具體使用

 更新時(shí)間:2026年03月18日 09:29:41   作者:兆子龍  
類型收窄是TypeScript類型系統(tǒng)中最為核心和強(qiáng)大的特性之一,本文主要介紹了TypeScript?類型收窄的具體使用,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

一、類型收窄概述

類型收窄是 TypeScript 類型系統(tǒng)中最為核心和強(qiáng)大的特性之一。它允許 TypeScript 在特定的代碼路徑中,根據(jù)條件判斷自動(dòng)縮小變量的類型范圍,從而提供更精確的類型信息和更安全的類型檢查。

在日常開(kāi)發(fā)中,我們經(jīng)常需要處理聯(lián)合類型、可選屬性、字面量類型等復(fù)雜類型場(chǎng)景。如果沒(méi)有類型收窄,我們不得不在每次使用時(shí)都進(jìn)行類型斷言,這不僅繁瑣,還容易引入運(yùn)行時(shí)錯(cuò)誤。類型收窄機(jī)制讓 TypeScript 能夠智能地推斷變量在特定上下文中的具體類型,大大提升了開(kāi)發(fā)效率和代碼質(zhì)量。

理解類型收窄不僅能幫助你寫出更簡(jiǎn)潔的代碼,還能讓你在面對(duì)復(fù)雜類型時(shí)游刃有余。本文將從基礎(chǔ)概念出發(fā),深入講解各種類型收窄技巧,并通過(guò)大量實(shí)例幫助你建立完整的類型收窄知識(shí)體系。

二、類型守衛(wèi)與類型收窄

2.1 什么是類型守衛(wèi)

類型守衛(wèi)(Type Guard)是一種特殊的表達(dá)式或函數(shù),它能夠告訴 TypeScript 編譯器某個(gè)變量在特定條件下具有更具體的類型。類型守衛(wèi)是實(shí)現(xiàn)類型收窄的主要手段。

TypeScript 內(nèi)置了多種類型守衛(wèi)機(jī)制,包括 typeof 操作符、instanceof 操作符、in 操作符等。同時(shí),我們也可以通過(guò)自定義類型守衛(wèi)函數(shù)來(lái)創(chuàng)建更復(fù)雜的類型判斷邏輯。

// typeof 類型守衛(wèi)
function processValue(value: string | number) {
  if (typeof value === 'string') {
    // 在這個(gè)分支中,value 被收窄為 string 類型
    return value.toUpperCase();
  } else {
    // 在這個(gè)分支中,value 被收窄為 number 類型
    return value * 2;
  }
}

// instanceof 類型守衛(wèi)
class Dog {
  bark() {
    console.log('Woof!');
  }
}

class Cat {
  meow() {
    console.log('Meow!');
  }
}

function makeSound(animal: Dog | Cat) {
  if (animal instanceof Dog) {
    animal.bark();
  } else {
    animal.meow();
  }
}

2.2 自定義類型守衛(wèi)

當(dāng)內(nèi)置的類型守衛(wèi)無(wú)法滿足需求時(shí),我們可以創(chuàng)建自定義的類型守衛(wèi)函數(shù)。自定義類型守衛(wèi)的返回值類型使用 value is Type 的形式,告訴 TypeScript 返回 true 時(shí)變量的具體類型。

interface Fish {
  swim(): void;
  hasFins: boolean;
}

interface Bird {
  fly(): void;
  hasWings: boolean;
}

function isFish(animal: Fish | Bird): animal is Fish {
  return (animal as Fish).swim !== undefined;
}

function handleAnimal(animal: Fish | Bird) {
  if (isFish(animal)) {
    // animal 被收窄為 Fish 類型
    animal.swim();
    console.log('Has fins:', animal.hasFins);
  } else {
    // animal 被收窄為 Bird 類型
    animal.fly();
    console.log('Has wings:', animal.hasWings);
  }
}

自定義類型守衛(wèi)在處理復(fù)雜類型判斷時(shí)非常有用,可以將類型判斷邏輯封裝成可復(fù)用的函數(shù),使代碼更加清晰和易于維護(hù)。

2.3 類型守衛(wèi)的組合使用

在實(shí)際開(kāi)發(fā)中,我們經(jīng)常需要組合使用多個(gè)類型守衛(wèi)來(lái)處理復(fù)雜的類型判斷場(chǎng)景。TypeScript 能夠正確地追蹤多個(gè)類型守衛(wèi)的組合效果,實(shí)現(xiàn)多層類型收窄。

type Shape = 
  | { kind: 'circle'; radius: number }
  | { kind: 'square'; side: number }
  | { kind: 'rectangle'; width: number; height: number };

function calculateArea(shape: Shape): number {
  if (shape.kind === 'circle') {
    // 收窄為 circle
    return Math.PI * shape.radius ** 2;
  }
  
  if (shape.kind === 'square') {
    // 收窄為 square
    return shape.side ** 2;
  }
  
  // 收窄為 rectangle
  return shape.width * shape.height;
}

// 組合使用多個(gè)條件
function describeShape(shape: Shape): string {
  if (shape.kind === 'circle' && shape.radius > 10) {
    return 'Large circle';
  }
  
  if (shape.kind === 'square' || shape.kind === 'rectangle') {
    if ('side' in shape) {
      return 'Square with side ' + shape.side;
    }
    return `Rectangle ${shape.width}x${shape.height}`;
  }
  
  return 'Unknown shape';
}

三、可辨識(shí)聯(lián)合與類型收窄

3.1 可辨識(shí)聯(lián)合模式

可辨識(shí)聯(lián)合(Discriminated Union)是 TypeScript 中處理相關(guān)類型的一種強(qiáng)大模式。它通過(guò)一個(gè)共有的字面量屬性(可辨識(shí)屬性)來(lái)區(qū)分聯(lián)合中的不同成員,TypeScript 可以根據(jù)這個(gè)屬性的值進(jìn)行精確的類型收窄。

// 定義可辨識(shí)聯(lián)合類型
type PaymentStatus = 
  | { status: 'pending' }
  | { status: 'processing' }
  | { status: 'success'; transactionId: string }
  | { status: 'failed'; errorCode: number; message: string };

// 處理支付狀態(tài)
function handlePayment(status: PaymentStatus) {
  switch (status.status) {
    case 'pending':
      console.log('Payment is pending...');
      break;
      
    case 'processing':
      console.log('Payment is being processed...');
      break;
      
    case 'success':
      // status 被收窄為 { status: 'success'; transactionId: string }
      console.log('Payment successful! Transaction ID:', status.transactionId);
      break;
      
    case 'failed':
      // status 被收窄為 { status: 'failed'; errorCode: number; message: string }
      console.log('Payment failed:', status.message, 'Code:', status.errorCode);
      break;
  }
}

可辨識(shí)聯(lián)合模式的核心在于可辨識(shí)屬性的選擇。這個(gè)屬性應(yīng)該是每個(gè)類型成員都擁有的字面量類型,且不同成員的值應(yīng)該互不相同。這樣 TypeScript 才能準(zhǔn)確地區(qū)分和收窄類型。

3.2 窮盡性檢查

可辨識(shí)聯(lián)合的一個(gè)強(qiáng)大特性是支持窮盡性檢查(Exhaustiveness Checking)。當(dāng)我們?cè)?switch 語(yǔ)句或 if-else 鏈中處理所有可能的類型成員時(shí),TypeScript 能夠確保我們不會(huì)遺漏任何情況。

type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';

function handleRequest(method: HTTPMethod, url: string, body?: unknown) {
  switch (method) {
    case 'GET':
      return fetch(url, { method: 'GET' });
      
    case 'POST':
      return fetch(url, { 
        method: 'POST',
        body: body ? JSON.stringify(body) : undefined 
      });
      
    case 'PUT':
      return fetch(url, { 
        method: 'PUT',
        body: body ? JSON.stringify(body) : undefined 
      });
      
    case 'DELETE':
      return fetch(url, { method: 'DELETE' });
      
    default:
      // 窮盡性檢查:如果遺漏了任何情況,這里會(huì)報(bào)錯(cuò)
      const _exhaustive: never = method;
      throw new Error(`Unknown HTTP method: ${_exhaustive}`);
  }
}

當(dāng)添加新的 HTTPMethod 類型時(shí),default 分支會(huì)產(chǎn)生類型錯(cuò)誤,提示開(kāi)發(fā)者需要處理新的情況。這種機(jī)制能夠有效防止遺漏處理新添加的類型成員。

3.3 嵌套可辨識(shí)聯(lián)合

復(fù)雜的業(yè)務(wù)場(chǎng)景往往需要使用嵌套的可辨識(shí)聯(lián)合來(lái)處理多層次的數(shù)據(jù)結(jié)構(gòu)。TypeScript 能夠正確地追蹤多層嵌套的類型收窄。

type APIResponse<T> = 
  | { success: true; data: T }
  | { success: false; error: { code: number; message: string } };

type User = {
  id: number;
  name: string;
  email: string;
};

type Order = {
  orderId: string;
  items: Array<{ productId: string; quantity: number }>;
  total: number;
};

type UserResponse = APIResponse<User>;
type OrderResponse = APIResponse<Order>;

function handleResponse<T>(
  response: APIResponse<T>,
  onSuccess: (data: T) => void,
  onError: (error: { code: number; message: string }) => void
) {
  if (response.success) {
    // response 被收窄為 { success: true; data: T }
    onSuccess(response.data);
  } else {
    // response 被收窄為 { success: false; error: { code: number; message: string } }
    onError(response.error);
  }
}

// 使用示例
const userResponse: UserResponse = {
  success: true,
  data: { id: 1, name: 'John', email: 'john@example.com' }
};

handleResponse(
  userResponse,
  (user) => console.log('User:', user.name),
  (error) => console.error('Error:', error.message)
);

四、類型謂詞與類型斷言

4.1 類型謂詞詳解

類型謂詞(Type Predicate)是自定義類型守衛(wèi)的核心語(yǔ)法,形式為 parameterName is Type。它允許我們精確地控制 TypeScript 如何收窄參數(shù)的類型。

interface Admin {
  role: 'admin';
  permissions: string[];
}

interface User {
  role: 'user';
  username: string;
}

interface Guest {
  role: 'guest';
  visitCount: number;
}

type AuthenticatedUser = Admin | User | Guest;

// 類型謂詞函數(shù)
function isAdmin(user: AuthenticatedUser): user is Admin {
  return user.role === 'admin';
}

function isUser(user: AuthenticatedUser): user is User {
  return user.role === 'user';
}

function isGuest(user: AuthenticatedUser): user is Guest {
  return user.role === 'guest';
}

// 使用類型謂詞
function getUserPermissions(user: AuthenticatedUser): string[] {
  if (isAdmin(user)) {
    // user 被收窄為 Admin
    return user.permissions;
  }
  
  if (isUser(user)) {
    // user 被收窄為 User
    return []; // 普通用戶沒(méi)有特殊權(quán)限
  }
  
  // user 被收窄為 Guest
  return [];
}

類型謂詞的強(qiáng)大之處在于它可以表達(dá)復(fù)雜的類型判斷邏輯,而不僅僅局限于簡(jiǎn)單的屬性檢查。這使得我們可以創(chuàng)建高度定制化的類型守衛(wèi)。

4.2 類型斷言的使用場(chǎng)景

類型斷言(Type Assertion)允許我們顯式地告訴 TypeScript 某個(gè)值的類型。與類型收窄不同,類型斷言是開(kāi)發(fā)者主動(dòng)告訴編譯器「我知道這個(gè)類型是什么」,而不是讓編譯器通過(guò)代碼邏輯推斷。

// 使用 as 語(yǔ)法進(jìn)行類型斷言
const myCanvas = document.getElementById('myCanvas') as HTMLCanvasElement;
const ctx = myCanvas.getContext('2d'); // 現(xiàn)在可以訪問(wèn) Canvas API

// 使用尖括號(hào)語(yǔ)法(僅在 .tsx 文件外可用)
const myInput = document.getElementById('myInput') as HTMLInputElement;
const value = myInput.value;

// 非空斷言
const elements = document.querySelectorAll('.item')!;
elements.forEach(el => el.textContent);

// 類型斷言 vs 類型守衛(wèi)
interface APIResponse {
  data: unknown;
}

function handleResponse(response: APIResponse) {
  // 類型斷言:開(kāi)發(fā)者確信 data 是 User 類型
  const user = response.data as User;
  console.log(user.name);
  
  // 類型守衛(wèi):通過(guò)代碼邏輯驗(yàn)證類型
  if (isUser(response.data)) {
    console.log(response.data.name); // TypeScript 已知這是 User
  }
}

需要注意的是,類型斷言是信任開(kāi)發(fā)者的,如果斷言錯(cuò)誤,可能導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。因此,應(yīng)該優(yōu)先使用類型守衛(wèi)進(jìn)行類型收窄,只有在確實(shí)知道類型的情況下才使用類型斷言。

4.3 雙重?cái)嘌耘c類型斷言的風(fēng)險(xiǎn)

在某些情況下,我們需要使用雙重?cái)嘌詠?lái)繞過(guò) TypeScript 的類型檢查。但這種做法存在風(fēng)險(xiǎn),應(yīng)該謹(jǐn)慎使用。

// 雙重?cái)嘌裕簭?A 類型斷言到 unknown,再斷言到目標(biāo)類型
const value = someValue as unknown as TargetType;

// 示例:處理 DOM 元素
const element = document.getElementById('my-element');
// 正常情況
const input = element as HTMLInputElement;

// 雙重?cái)嘌裕寒?dāng)你確定元素是特定類型,但 TypeScript 無(wú)法推斷時(shí)
const widget = element as unknown as CustomWidget;

// 使用雙重?cái)嘌缘娘L(fēng)險(xiǎn)
// 如果元素實(shí)際上不是 CustomWidget 類型,運(yùn)行時(shí)可能出錯(cuò)

雙重?cái)嘌詰?yīng)該作為最后手段使用。在使用之前,應(yīng)該考慮是否有更好的類型設(shè)計(jì)方式,或者是否可以通過(guò)類型守衛(wèi)來(lái)實(shí)現(xiàn)類型收窄。

五、索引訪問(wèn)類型與映射類型

5.1 索引訪問(wèn)類型

索引訪問(wèn)類型(Indexed Access Types)允許我們通過(guò)索引獲取類型的成員,從而創(chuàng)建更靈活的類型定義。

interface Person {
  name: string;
  age: number;
  address: {
    city: string;
    zip: string;
  };
}

// 獲取單個(gè)屬性類型
type PersonName = Person['name']; // string
type PersonAge = Person['age']; // number

// 獲取嵌套屬性類型
type PersonCity = Person['address']['city']; // string

// 使用聯(lián)合類型作為索引
type PersonAddressKeys = Person['address'][keyof Person['address']]; // string

// 獲取數(shù)組元素類型
type StringArray = string[];
type StringArrayElement = StringArray[number]; // string

// 在函數(shù)中使用索引訪問(wèn)類型
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const person: Person = {
  name: 'Alice',
  age: 30,
  address: { city: 'NYC', zip: '10001' }
};

const name = getProperty(person, 'name'); // string
const city = getProperty(person, 'address'); // { city: string; zip: string }

5.2 映射類型

映射類型(Mapped Types)允許我們基于現(xiàn)有類型創(chuàng)建新類型,對(duì)類型的每個(gè)屬性進(jìn)行轉(zhuǎn)換。

interface Original {
  id: number;
  name: string;
  age: number;
  email: string;
}

// 將所有屬性變?yōu)榭蛇x
type Partial<T> = {
  [P in keyof T]?: T[P];
};

type PartialPerson = Partial<Person>;
// { id?: number; name?: string; age?: number; email?: string }

// 將所有屬性變?yōu)橹蛔x
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

type ReadonlyPerson = Readonly<Person>;
// { readonly id: number; readonly name: string; ... }

// 將所有屬性變?yōu)榭蛇x且只讀
type OptionalReadonly<T> = {
  readonly [P in keyof T]?: T[P];
};

// 映射類型的內(nèi)置實(shí)現(xiàn)
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

type PersonBasic = Pick<Person, 'id' | 'name'>;
// { id: number; name: string }

type Omit<T, K extends keyof T> = {
  [P in keyof T as P extends K ? never : P]: T[P];
};

type PersonWithoutEmail = Omit<Person, 'email'>;
// { id: number; name: string; age: number }

5.3 條件類型與分布式特性

條件類型(Conditional Types)是 TypeScript 類型系統(tǒng)中最靈活的特性之一,它允許我們根據(jù)條件選擇不同的類型。

// 條件類型基本語(yǔ)法
type IsString<T> = T extends string ? true : false;

type Test1 = IsString<string>; // true
type Test2 = IsString<number>; // false

// 分布式條件類型
// 當(dāng) T 是聯(lián)合類型時(shí),條件類型會(huì)分別應(yīng)用于每個(gè)成員
type ToArray<T> = T[];

type Test3 = ToArray<string | number>; // string[] | number[]

// 正確實(shí)現(xiàn):分布式
type ToArrayDistributed<T> = T extends any ? T[] : never;

type Test4 = ToArrayDistributed<string | number>; // string[] | number[]

// 實(shí)際應(yīng)用:根據(jù)類型選擇不同的處理方式
type Response<T> = T extends string 
  ? { type: 'text'; content: T }
  : { type: 'binary'; data: T };

type TextResponse = Response<string>;
// { type: 'text'; content: string }

type BinaryResponse = Response<Blob>;
// { type: 'binary'; data: Blob }

六、模板字面量類型與類型收窄

6.1 模板字面量類型基礎(chǔ)

TypeScript 4.1 引入了模板字面量類型(Template Literal Types),允許我們?cè)陬愋图?jí)別使用模板字面量語(yǔ)法。這為類型系統(tǒng)帶來(lái)了很大的字符串處理能力。

// 基本模板字面量類型
type Greeting = `Hello, ${string}`;
// 匹配任何以 "Hello, " 開(kāi)頭的字符串

type Email = `${string}@${string}.${string}`;
// 匹配基本的 email 格式

// 使用聯(lián)合類型
type EventName = `on${'Click' | 'Hover' | 'Focus'}`;
// "onClick" | "onHover" | "onFocus"

// 模板字面量類型與收窄
type CSSProperty = 
  | `${'margin' | 'padding'}-${'top' | 'right' | 'bottom' | 'left'}`;
// "margin-top" | "margin-right" | ... | "padding-left"

function setCSSProperty(prop: CSSProperty, value: string) {
  // 在函數(shù)內(nèi)部,prop 被收窄為具體的 CSS 屬性名
  document.documentElement.style.setProperty(`--${prop}`, value);
}

setCSSProperty('margin-top', '10px'); // OK
setCSSProperty('padding-left', '20px'); // OK
// setCSSProperty('border-radius', '5px'); // Error

6.2 模板字面量類型的高級(jí)用法

模板字面量類型可以與條件類型、映射類型等組合使用,創(chuàng)建復(fù)雜的類型轉(zhuǎn)換邏輯。

// 使用 infer 進(jìn)行類型推斷
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

type Func1 = () => number;
type Func2 = (x: string) => boolean;
type Func3 = (a: number, b: string) => { name: string };

type R1 = GetReturnType<Func1>; // number
type R2 = GetReturnType<Func2>; // boolean
type R3 = GetReturnType<Func3>; // { name: string }

// 模板字面量類型中的 infer
type ExtractEventParams<T> = T extends `on${infer Event}` ? Event : never;

type Events = 'onClick' | 'onHover' | 'onSubmit' | 'customEvent';

type EventNames = ExtractEventParams<Events>;
// "Click" | "Hover" | "Submit" | "customEvent"

// 組合使用:創(chuàng)建完整的 API 路徑類型
type HTTPMethod = 'get' | 'post' | 'put' | 'delete';
type BasePath = '/api' | '/v2';
type Resource = 'users' | 'posts' | 'comments';

type APIEndpoint = `${Uppercase<HTTPMethod>} ${BasePath}/${Resource}`;
// "GET /api/users" | "GET /api/posts" | ... | "DELETE /v2/comments"

6.3 字符串類型收窄的實(shí)際應(yīng)用

模板字面量類型在處理字符串字面量類型時(shí)特別有用,可以實(shí)現(xiàn)精確的字符串類型收窄。

// 狀態(tài)機(jī)類型
type State = 
  | { kind: 'idle' }
  | { kind: 'loading' }
  | { kind: 'success'; data: unknown }
  | { kind: 'error'; message: string };

// 使用模板字面量類型創(chuàng)建 Action 類型
type Action = 
  | { type: 'FETCH_START' }
  | { type: 'FETCH_SUCCESS'; payload: unknown }
  | { type: 'FETCH_ERROR'; error: string }
  | { type: 'RESET' };

// Action 處理函數(shù)
function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'FETCH_START':
      return { kind: 'loading' };
      
    case 'FETCH_SUCCESS':
      return { kind: 'success', data: action.payload };
      
    case 'FETCH_ERROR':
      return { kind: 'error', message: action.error };
      
    case 'RESET':
      return { kind: 'idle' };
      
    default:
      // 窮盡性檢查
      const _: never = action;
      return state;
  }
}

七、類型收窄在泛型中的應(yīng)用

7.1 泛型約束與類型收窄

泛型與類型收窄的結(jié)合是 TypeScript 類型系統(tǒng)的高級(jí)應(yīng)用。通過(guò)泛型約束和類型守衛(wèi),我們可以創(chuàng)建靈活且類型安全的通用函數(shù)。

interface HasId {
  id: number;
}

interface HasName {
  name: string;
}

type WithId<T> = T extends HasId ? T : never;
type WithName<T> = T extends HasName ? T : never;

// 條件類型實(shí)現(xiàn)類型收窄
function processItem<T>(item: T): T extends HasId ? T : never {
  if ('id' in item) {
    return item as T extends HasId ? T : never;
  }
  throw new Error('Item must have id');
}

// 使用泛型約束
function getId<T extends HasId>(item: T): number {
  return item.id;
}

function getName<T extends HasName>(item: T): string {
  return item.name;
}

// 泛型函數(shù)中的類型收窄
function firstItem<T>(arr: T[]): T | undefined {
  return arr[0];
}

function processArray<T extends HasId>(arr: T[]): number[] {
  const first = firstItem(arr);
  if (first) {
    // first 被收窄為 T & HasId
    return [first.id];
  }
  return [];
}

7.2 多泛型參數(shù)的類型收窄

當(dāng)函數(shù)有多個(gè)泛型參數(shù)時(shí),類型收窄變得更加復(fù)雜。我們需要考慮不同參數(shù)之間的類型關(guān)系。

// 關(guān)聯(lián)類型的類型收窄
function pair<T, U extends T>(first: T, second: U): [T, U] {
  return [first, second];
}

const p1 = pair('hello', 'world'); // [string, string]
const p2 = pair(42, 100); // [number, number]
// const p3 = pair('hello', 42); // Error: second must extend first

// 使用類型守衛(wèi)進(jìn)行多泛型收窄
function isSameType<T, U>(a: T, b: U): a is T & U {
  return typeof a === typeof b;
}

function merge<T, U>(a: T, b: U): T & U {
  if (isSameType(a, b)) {
    // a 和 b 被收窄為相同類型
    return { ...a, ...b } as T & U;
  }
  return { ...a, ...b } as T & U;
}

7.3 泛型工廠函數(shù)中的類型收窄

泛型工廠函數(shù)是創(chuàng)建類型安全對(duì)象的強(qiáng)大工具,結(jié)合類型收窄可以實(shí)現(xiàn)復(fù)雜的類型轉(zhuǎn)換邏輯。

// 工廠函數(shù)模式
interface Factory<T> {
  create(): T;
}

class StringFactory implements Factory<string> {
  create() {
    return 'default string';
  }
}

class NumberFactory implements Factory<number> {
  create() {
    return 42;
  }
}

function createFactory<T>(factory: Factory<T>): T {
  return factory.create();
}

// 使用泛型工廠
const str = createFactory(new StringFactory()); // string
const num = createFactory(new NumberFactory()); // number

// 泛型與條件類型
type FactoryReturn<T> = T extends Factory<infer R> ? R : never;

type StringReturn = FactoryReturn<StringFactory>; // string
type NumberReturn = FactoryReturn<NumberFactory>; // number

八、類型收窄的最佳實(shí)踐

8.1 何時(shí)使用類型收窄

類型收窄應(yīng)該成為 TypeScript 開(kāi)發(fā)的日常實(shí)踐。以下是一些適合使用類型收窄的場(chǎng)景。

處理聯(lián)合類型時(shí),類型收窄是最自然的選擇。無(wú)論是可選屬性、可空值還是多態(tài)類型,類型收窄都能提供精確的類型信息。

// 處理可選屬性
interface User {
  name: string;
  email?: string;
}

function sendEmail(user: User) {
  if (user.email) {
    // user.email 被收窄為 string(不再是 string | undefined)
    sendToAddress(user.email);
  }
}

// 處理 null | undefined
function processValue(value: string | null | undefined) {
  if (value != null) {
    // value 被收窄為 string
    console.log(value.toUpperCase());
  }
}

8.2 避免過(guò)度類型斷言

雖然類型斷言是 TypeScript 的重要特性,但應(yīng)該優(yōu)先使用類型收窄。類型斷言相當(dāng)于告訴編譯器「我知道我在做什么」,這意味著放棄了 TypeScript 的類型檢查。

// 不推薦:過(guò)度使用類型斷言
function badExample(value: unknown) {
  const str = value as string; // 危險(xiǎn)!如果 value 不是 string,運(yùn)行時(shí)可能出錯(cuò)
  return str.toUpperCase();
}

// 推薦:使用類型守衛(wèi)
function goodExample(value: unknown) {
  if (typeof value === 'string') {
    return value.toUpperCase();
  }
  return 'Not a string';
}

// 如果必須使用類型斷言,使用類型斷言函數(shù)封裝
function assertString(value: unknown): asserts value is string {
  if (typeof value !== 'string') {
    throw new Error('Expected string');
  }
}

function anotherExample(value: unknown) {
  assertString(value);
  // 現(xiàn)在 value 被收窄為 string
  return value.toUpperCase();
}

8.3 利用類型系統(tǒng)發(fā)現(xiàn)潛在問(wèn)題

類型收窄不僅是一種編碼技巧,更是一種發(fā)現(xiàn)潛在問(wèn)題的工具。當(dāng) TypeScript 無(wú)法正確收窄類型時(shí),往往意味著代碼中存在設(shè)計(jì)問(wèn)題或邏輯漏洞。

// TypeScript 無(wú)法收窄時(shí),可能需要重新設(shè)計(jì)類型
type Response<T> = 
  | { success: true; data: T }
  | { success: false; error: string };

function handleResponse<T>(response: Response<T>) {
  if (response.success) {
    return response.data;
  }
  
  // 在這里,response 被收窄為 { success: false; error: string }
  // 如果忘記處理 error 情況,TypeScript 不會(huì)報(bào)錯(cuò)
  // 但我們可以添加默認(rèn)處理
  throw new Error(response.error || 'Unknown error');
}

九、總結(jié)

類型收窄是 TypeScript 類型系統(tǒng)的核心特性,它讓我們能夠在代碼中實(shí)現(xiàn)精確的類型推斷和安全的類型檢查。從簡(jiǎn)單的 typeof 檢查到復(fù)雜的可辨識(shí)聯(lián)合,從類型謂詞到模板字面量類型,TypeScript 提供了豐富的工具來(lái)處理各種類型場(chǎng)景。

掌握類型收窄不僅能寫出更安全的代碼,還能提升開(kāi)發(fā)效率。當(dāng)你熟悉了類型收窄的技巧后,你會(huì)發(fā)現(xiàn)處理復(fù)雜類型變得自然而直觀。同時(shí),類型收窄也是理解 TypeScript 高級(jí)類型系統(tǒng)的基礎(chǔ),對(duì)于深入學(xué)習(xí) TypeScript 至關(guān)重要。

在實(shí)際開(kāi)發(fā)中,應(yīng)該養(yǎng)成使用類型收窄的習(xí)慣,優(yōu)先選擇類型守衛(wèi)而非類型斷言,讓 TypeScript 的類型系統(tǒng)為你的代碼保駕護(hù)航。

到此這篇關(guān)于TypeScript 類型收窄的具體使用的文章就介紹到這了,更多相關(guān)TypeScript 類型收窄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關(guān)文章

最新評(píng)論

遵义市| 凤阳县| 洛扎县| 六枝特区| 久治县| 巴东县| 综艺| 林州市| 鞍山市| 龙门县| 田东县| 法库县| 阿鲁科尔沁旗| 峨山| 衢州市| 邵阳县| 辰溪县| 潮安县| 遂宁市| 错那县| 庆城县| 左贡县| 和静县| 淳化县| 许昌县| 枣庄市| 四会市| 东安县| 报价| 易门县| 潍坊市| 遂溪县| 抚顺市| 遂平县| 天台县| 澳门| 克山县| 营口市| 申扎县| 浦江县| 噶尔县|