Day22 : 【TypeScript 学起来】Generic Function 泛型函式

上一篇介绍了 Generic 泛型, 其实这篇差不多意思 XDD 主要针对 Generic Function。
若有错误,欢迎留言指教,感恩的心。


Generic Function 初体验

以下例子想要取得带入参数 array 第0个的值, 我们可以看到s回传值的型别会是 any, 但我想要带入不同的值对应的型别,该怎麽做呢。

function firstElement(arr: any[]) {
  return arr[0];
}

const s = firstElement(["a", "b", "c"]);
const n = firstElement([1, 2, 3]);

console.log(s); //a
console.log(n); //1

我们可以使用泛型,我们在函式名後添加了 ,其中 Type 用来指代任意输入的型别,让型别推论自动推算出来。 可以看到变数s会是string型别。而n会是number型别。

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

// s is of type 'string'
const s = firstElement(["a", "b", "c"]);
// n is of type 'number'
const n = firstElement([1, 2, 3]);

console.log(s); //a
console.log(n); //1


Inference

除了使用<Type>的方式, 可以传入多组参数如<Input, Output>,让 TypeScript 自动 inferred 型别。

如下,n 被 inferred 为 string, 而透过 parseInt 字串转数字,最後 parsed 被 inferred 的型别是 number[]InputOutput 命名可以自行定义。

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

// Parameter 'n' is of type 'string'
// 'parsed' is of type 'number[]'
const parsed = map(["1", "2", "3"], (n) => parseInt(n, 10));

console.log(parsed); //[1,2,3]

Constraints 型别约束

以下函式有两个传入参数 ab, 我们预期他们传入的型别都要有 length 属性,我们这时候可以使用 extends 关键字来限制参数length 属性,且指定length是 number 型别。

我们可以看到只有 notOK 会报错,因为传入的参数 (10, 100) 不符 length 属性。

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

// longerArray is of type 'number[]'
const longerArray = longest([1, 2], [1, 2, 3]);
console.log(longerArray); //[1,2,3]

// longerString is of type 'alice' | 'bob'
const longerString = longest("alice", "bob");
console.log(longerString); //alice

// Error! Numbers don't have a 'length' property
const notOK = longest(10, 100);
//error: Argument of type 'number' is not assignable to parameter of type '{ length: number; }'.

在官网有举例在使用generic泛型约束的时候,可能会出错的地方,可参考 这里


Specifying Type Arguments 指定参数型别

在使用 generic 中有些情况 TypeScript 无法自动inferred型别,我们可以自行指定:

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

const arr = combine([1, 2, 3], ["hello"]);
//❌ error: Type 'string' is not assignable to type 'number'.


const arr = combine<string | number>([1, 2, 3], ["hello"]);
console.log(arr); //[1, 2, 3, "hello"]
// ⭕ 指定他为 string | number 联合型别。


写出好的 Generic Function

1.Push Type Parameters Down

//⭕ 返回型别为 Type
function firstElement1<Type>(arr: Type[]) {
  return arr[0];
}

//❌ 返回型别为 any
function firstElement2<Type extends any[]>(arr: Type) {
  return arr[0];
}

// a: number (good)
const a = firstElement1([1, 2, 3]);
// b: any (bad)
const b = firstElement2([1, 2, 3]);

2. Use Fewer Type Parameters

Always use as few type parameters as possible.

//⭕ 
function filter1<Type>(arr: Type[], func: (arg: Type) => boolean): Type[] {
  return arr.filter(func);
}
 
//❌ 
function filter2<Type, Func extends (arg: Type) => boolean>(
  arr: Type[],
  func: Func
): Type[] {
  return arr.filter(func);
}

3. Type Parameters Should Appear Twice

If a type parameter only appears in one location, strongly reconsider if you actually need it.

如果只使用到一种型别,我们应该用简单的方式去写即可。

//❌ 
function greet1<Str extends string>(s: Str) {
  console.log("Hello, " + s);
}
greet1("world");

//⭕
function greet2(s: string) {
  console.log("Hello, " + s);
}
greet2("world");

Day22 done. 感谢阅读,明天见!


参考资料

https://www.typescriptlang.org/docs/handbook/2/functions.html#generic-functions


<<:  【在厨房想30天的演算法】Day 22 演算法 : 最短路径 Shortest Path Bellman–Ford 演算法

>>:  成为工具人应有的工具包-22 OpenedFilesView

2.4.10 Design System - Input Text

以前我绝对是对哲学避之唯恐不及的 但某一年意外看到「正义 一场思辨之旅」以及「超译 尼采」後 开始...

Day 21 : 模型优化 - 剪枝 Pruning

如果说可以让模型缩小10倍,精度还维持水准,这是什麽巫术? 延续 Day 20 的模型优化作法,本...

[Day4] 实作 - 主角篇

首先先在plugins/底下创立一个档案叫做ActionBattle_Actor.js 接着用昨天的...

Day2 工业控制网路 vs IT 资讯网路

工业控制网路 vs IT 资讯网路 管理目标 工业控制网路的设备需要高可用性,因为工厂设备若停摆,...

[Day3] Vite 出小蜜蜂~ Game Loop!

Day3 软件架构 这边卡比要介绍一个名词, Software Architecture 软件架构。...