[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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...