[TS手冊學習] 03_函數相關知識點

来源:https://www.cnblogs.com/feixianxing/archive/2023/11/30/typescript-handbook-function.html
-Advertisement-
Play Games

TS中的函數需要聲明參數列表和返回值的類型,除此只要,還有關於泛型、可選參數、不定長參數列表、回調函數、this、重載的聲明規則。 ...


TS官方手冊:TypeScript: Handbook - The TypeScript Handbook (typescriptlang.org)

函數類型表達式

使用類似於箭頭表達式的形式來描述一個函數的類型。

function greeter(fn: (a: string) => void) {
  fn("Hello, World");
}

上述代碼中,fn: (a:string) => void表示變數fn是一個函數,這個函數有一個參數a,是string類型,且這個函數的返回值類型為void,即沒有返回值。

調用簽名

在 JS 中,函數是對象,除了可以調用也可以擁有自己的屬性。而使用函數類型表達式無法聲明這一部分屬性的類型。

可以將函數視為一個對象,聲明一個類型,其中包含多個屬性的類型聲明,並使用調用簽名來描述函數參數和返回值的類型,取代原先函數類型表達式的寫法。

type DescribableFunction = {
  description: string;
  (someArg: number): boolean;
};

在這個例子中,DescribableFunction是一個函數類型,description是這個函數類型實例對象的一個屬性名,類型為string。而這個函數的參數列表類型聲明為:(someArg: number),返回值類型為boolean

需要註意,在這種寫法中,參數列表和返回值類型之間是用:隔開,而函數類型表達式是使用=>

構造簽名

搭配構造函數使用,在調用簽名的語法前面加上new

type SomeConstructor = {
  new (s: string): SomeObject;
};

泛型函數

如果函數的參數類型與返回值的參數類型存在關聯,可以使用泛型:

function firstElement<Type>(arr: Type[]): Type | undefined {
  return arr[0];
}

// s是'string'類型
const s = firstElement(["a", "b", "c"]);
// n是'number'類型
const n = firstElement([1, 2, 3]);
// u是undefined類型
const u = firstElement([]);

多類型

function map<Input, Output>(arr: Input[], func: (arg: Input) => Output): Output[] {
  return arr.map(func);
}

類型約束

泛型支持函數傳入不同的類型,當需要約束時,例如要求傳入的參數類型必須包含一個某類型的屬性,則可以:

function longest<Type extends { length: number }>(a: Type, b: Type) {
  if (a.length >= b.length) {
    return a;
  } else {
    return b;
  }
}

使用<Type extends { x : y}>實現,extends表示繼承於類型{x:y},表示類型Type應該包含y類型的屬性x

需要註意如果函數返回值類型為Type,那麼不能返回類型為{x:y},因為Type包含{x:y},而可能存在比{x:y}更多的屬性。

指定類型參數

function combine<Type>(arr1: Type[], arr2: Type[]): Type[] {
  return arr1.concat(arr2);
}

// 報錯,因為根據第一次參數,Type會被識別為number,但是第二個參數卻是string[]類型。
const arr = combine([1, 2, 3], ["hello"]);

如果執意這麼設計函數的話,可以考慮使用聯合類型:

const arr = combine<string | number>([1, 2, 3], ["hello"]);
使用泛型函數的建議
  1. 儘可能使用類型參數本身,而不去使用類型約束。

    // Good: 返回值類型會被推斷為Type
    function firstElement1<Type>(arr: Type[]) {
      return arr[0];
    }
    // Bad: 返回值類型會被推斷為any
    function firstElement2<Type extends any[]>(arr: Type) {
      return arr[0];
    }
    
  2. 儘可能少地使用類型參數。

    過多的類型參數會使得函數難以閱讀,儘量確保類型參數與多個值相關(例如與函數參數和返回值都有關)再使用。

    // Good: 只用了Type一個類型參數
    function filter1<Type>(arr: Type[], func: (arg: Type) => boolean): Type[] {
      return arr.filter(func);
    }
    // Bad: Func這個類型參數是多餘的,只用在了一個函數參數
    function filter2<Type, Func extends (arg: Type) => boolean>(
      arr: Type[],
      func: Func
    ): Type[] {
      return arr.filter(func);
    }
    
  3. 如果類型參數只出現在一個位置,那麼這個類型參數很可能不是必要的。

    使用泛型是因為函數中有若幹個值的類型存在關聯,如果類型參數只出現在一個位置,很可能不是必要的。

    // Bad: Str是不必要的
    function greet<Str extends string>(s: Str) {
      console.log("Hello, " + s);
    }
    // Good
    function greet(s: string) {
        console.log("Hello, " + s);
    }
    

可選參數列表

function f(x?: number) {
  // ...
}
f(); // OK
f(10); // OK

:上面的代碼中x的類型實際為number|undefined,當不傳入該參數的時候就是undefined

如果考慮設置預設值,如下,那麼x的類型就會變成number,排除了undefined的情況。

function f(x = 10) {
  // ...
}

:只要一個參數是可選的,那麼這個參數就可以被傳入undefined

回調函數的可選參數

在設計一個回調函數的函數類型時,不要使用可選參數。

函數重載

function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
  if (d !== undefined && y !== undefined) {
    return new Date(y, mOrTimestamp, d);
  } else {
    return new Date(mOrTimestamp);
  }
}
const d1 = makeDate(12345678);
const d2 = makeDate(5, 5, 5);
// 報錯
const d3 = makeDate(1, 3);

如上述代碼,先寫兩個重載函數簽名(overload signatures),然後再寫一個函數相容實現這兩個簽名,叫做實現簽名(implementation signature)。

在調用函數的時候,需要以重載簽名為標準,不能以實現簽名為標準。也就是說,上面這段代碼中的函數makeDate,要麼傳入1個參數,要麼傳入3個參數,不能傳入2個參數。

  • 從外部無法看見實現的簽名。在編寫重載函數時,應該始終在函數的實現之上有兩個或多個簽名。
  • 實現簽名與重載簽名之間要相容。
  • 當可以使用聯合類型函數參數解決問題時,就不要使用函數重載。

聲明this的類型

const db = getDB();
const admins = db.filterUsers(function (this: User) {
  return this.admin;
});

其它與函數相關的類型

void

void作為函數的返回值類型,表示函數沒有返回值。

在 JS 中沒有返回值的函數會返回 undefined,但是在 TS 中undefinedvoid是不同的。

當函數返回值聲明為void,仍可以在函數體中return內容,但不管返回了什麼值,最終接收函數返回值的那個變數都會是void類型。

type voidFunc = ()=>void;
const f: voidFunc = ()=> true;

// 這裡value的類型會被推斷為void
const value = f();
object

object類型是除了stringnumberbigintbooleansymbolnullundefined的其它類型。

object類型與空對象類型{}不同,與全局類型Object也不同。永遠不要使用Object類型,而是使用object

在 JS 中,函數也是對象;在 TS 中,函數也被認為是object類型。

unknown

unknownany非常類似,但是unknown更安全。

因為unknown類型變數的任何操作都是非法的,這迫使大多數操作之前需要對unknown類型變數進行類型的檢查。

any類型的值執行操作之前不需要進行任何檢查。

function f1(a: any) {
  a.b(); // OK
}

function f2(a: unknown) {
  a.b(); // ERROR: 'a' is of type 'unknown'.
}

unknown類型只能賦值給anyunknown類型。

unknown類型的意義:TS 不允許我們對類型為 unknown 的值執行任意操作。我們必須首先執行某種類型檢查以縮小我們正在使用的值的類型範圍。

可以使用類型收束(Narrowing)的操作將unknown縮小到具體的類型,再進行後續操作。

never

never通常描述返回值類型,表示永遠不返回值。與void不同,使用never意味著函數會拋出一個異常,或者程式會被終止。

function fail(msg: string): never {
  throw new Error(msg);
}

另一種情況下也會出現never,就是當聯合類型被不斷收窄到空時,就是never

function fn(x: string | number) {
  if (typeof x === "string") {
    // do something
  } else if (typeof x === "number") {
    // do something else
  } else {
    x; // 這裡x的類型是'never'
  }
}
Function

全局類型Function聲明的變數包含了 JS 中函數所擁有的所有屬性和方法,例如bindcallapply

Function聲明的變數是可執行的,並且返回any

這種函數類型聲明方式很不安全,因為返回any,最好使用函數類型表達式聲明:()=>void

function doSomething(f: Function) {
  return f(1, 2, 3);
}

不定長參數列表

在 TS 中,不定長參數列表的類型應該被聲明為Array<T>T[]或元組類型。

function multiply(n: number, ...m: number[]) {
  return m.map((x) => n * x);
}

spread語法可以展開可迭代對象(例如數組,對象)變成不定長的參數列表。例如push函數可以接收多個參數。

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
arr1.push(...arr2);

但需要註意,TS 認為數組是可變的,數組長度是可變的。

觀察下麵的案例代碼:

const args = [8, 5];
const angle = Math.atan2(...args);

儘管Math.atan2接收兩個number類型的參數,而args也剛好是長度為2的number[]類型數組,展開後剛好。

但是這段代碼會報錯,因為數組是可變的。

一種較為直接的解決方法是使用const

// 視為長度為2的元組
const args = [8, 5] as const;
// 現在不會報錯了
const angle = Math.atan2(...args);

參數解構的類型聲明

function sum({ a, b, c }: { a: number; b: number; c: number }) {
  console.log(a + b + c);
}

或者使用type簡化:

type ABC = { a: number; b: number; c: number };
function sum({ a, b, c }: ABC) {
  console.log(a + b + c);
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在Linux伺服器上查詢進程,有以下幾種方法: 使用ps命令。這個命令用於報告當前系統的進程狀態。可以用以下方式使用ps命令來查看進程信息: ps aux:顯示系統中所有進程的信息。 ps -e:顯示所有進程的信息。 ps -f:顯示進程的所有信息。 ps -l:以長格式顯示進程信息。 ps -r: ...
  • SQL SELECT INTO 語句 SELECT INTO 語句將數據從一個表複製到一個新表中。 SELECT INTO 語法 將所有列複製到新表中: SELECT * INTO newtable [IN externaldb] FROM oldtable WHERE condition; 只複製 ...
  • 本文分享自華為雲社區《GaussDB(DWS)性能調優:常量標量子查詢做全連接導致整體慢》,作者: Zawami 。 問題描述 由於SQL中存在標量子查詢同另一查詢做笛卡爾積使SQL整體慢。標量子查詢,即結果集只有一行一列的子查詢。這裡導致的SQL語句執行慢不只是在於做笛卡爾積慢,也會使後續聚合更慢 ...
  • MySQL Shell如何接管手動搭建(含仲裁節點)MGR集群 本文源自GreatSQL社區用戶的一次提問: Q:一個包含仲裁節點(ARBITRATOR)的GreatSQL MGR集群,一開始是用手動方式構建,後來想用MySQL Shell接管,可以嗎? A:是可以的,不過也有一定局限性 具體的操作 ...
  • 優化器的作用是優化查詢語句的執行效率,它通過評估不同的執行計劃並選擇最優的執行計劃來實現這一目標。 CBO: 一種基於成本的優化器,它通過評估不同查詢執行計劃的成本來選擇最優的執行計劃。CBO會根據資料庫系統定義的統計信息以及其他因素,對不同的執行計划進行評估,並選擇成本最低的執行計劃。CBO的目標 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 上家公司有個需求是批量導出學生的二維碼,我一想這簡單啊,不就是先批量獲取學生數據,然後根據QRcode生成二維碼,然後在用html2canvas導出成圖片嘛。 由於公司工具庫有現成的生成壓縮包方法,我只需要獲得對應的圖片blob就可 ...
  • 【whistle 官網】http://wproxy.org/whistle/install.html 【用途】 抓包、mock、修改返回數據、修改響應頭欄位、延遲模擬弱網等 代理轉發 - 需要配置代理轉發規則 把某環境僅前端資源的請求代理轉發到本地 把某個介面地址的請求代理轉發到指定的後端環境地址 ...
  • 對象類型通常使用interface聲明,可以設置屬性為可選的或者只讀的,可以設置索引簽名。從簡單類型生成複雜類型可以使用類型繼承或者交集類型。提高類型的泛用性可以使用泛型。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...