0%

函数

写一个好的泛型函数建议:

类型参数下移:

1
2
3
4
5
6
7
8
9
10
11
function firstElemet1<Type>(arr:Type[]){
return arr[0];
}
function firstElemet2<Type extends any[]>(arr:Type){
return arr[0];
}
//a:number(good)
const a=firstElemet1([1,2,3])
//b:any(bad)
const b=firstElemet2([1,2,3])

第一个函数可以推断出返回的类型是number,第二个函数推断出的返回类型却是any,因为TypeScript不得不用约束的类型来推断arr[0]表达式,而不是等到函数调用的时候再去推断这个元素。

push down就是如果超类中的某个函数只与一个或者少数几个子类有关,那么最好将其从超类中挪走,放到真正关心它的子类中,即只在超类保留共用的行为,这种将超类中的函数本体复制到具体需要的子类的方法称为”push down”,与本节中的去除extend any[],将其具体的推断交给Type自身就类似于push down.

注意:如果可能,直接使用类型参数而不是约束它

使用更少的类型参数

1
2
3
4
5
6
7
8
9
10
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);
}

我们创建了一个并没有关联两个值的类型参数Func,这是一个危险信号,因为它意味着调用者不得不毫无理由的手动指定一个额外的类型参数,Func什么也没做,却导致函数更难阅读和推断

尽可能使用更少的类型参数

类型参数应该出现两次

有的时候一个函数其实并不需要泛型

1
2
3
4
function greet<Str extends string>(s:Str){
console.log("Hello,"+s);
}
greet("world");

类型参数时用来关联多个值之间的类型,如果一个类型参数只在函数签名里出现了一次,那它就没有跟任何东西产生关联

1
2
3
4
function greet(s:string){
console.log("Hello,"+s);

}

在函数中声明this

TypeScript会通过代码流反洗函数中的this会是什么类型

1
2
3
4
5
6
7
const user={
id:124,
admin:false,
becomeAdmin:function(){
this.admin=true;
}
}

TypeScript能够理解函数user.becomeAdmin中的this指向的是外层的对象user,在Js中,this是保留字,不能当做参数使用,但TypeScript可以允许你在函数体内声明this的类型

1
2
3
4
5
6
7
interface DB{
filterUsers(filters:(this:User)=>boolean):User[];
}
const db=getDB();
const admins=db.filterUsers(function(this:User){
return this.admin;
})

function的形式不能使用箭头函数

1
2
3
4
5
6
7
8
interface DB {
filterUsers(filter: (this: User) => boolean): User[];
}

const db = getDB();
const admins = db.filterUsers(() => this.admin);
// The containing arrow function captures the global value of 'this'.
// Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.

函数的可赋值性:

返回void

当基于上下文的类型推导推导出返回类型为void的时候,并不会强制函数一定不能返回内容,换句话说,如果一个返回void类型的函数类型(type vf=()=>void),当被应用时,也是可以返回任何值的,但返回的值会被忽略,在TS里,子类型的值可以赋值给父类型的变量,在这里,我们可以认为void类型比具体的一些类型,比如number,string类型之类的,更宽泛,因此可以接受这些具体类型。

虽然可以赋值,但是执行函数后返回值的类型,却是void的,并不是实际的真实类型,参考下面的f1变量,它并不是boolean类型,不能访问boolean类型的属性。void类型的用法,主要用在我们不希望调用者关心函数返回值的情况下,比如通常的异步回调函数,这种情况下返回值时没有意义的

1
2
3
4
5
6
7
8
9
10
11
type voidFunc = () => void;

const f1: voidFunc = () => {
return true;
};

const f2: voidFunc = () => true;

const f3: voidFunc = function () {
return true;
};

而且即便这些函数的返回值赋值给其他变量,也会维持void类型

1
2
3
const v1=f1();
const v2=f2();
const v3=f3();

因此,以下代码有效:

1
2
3
const src=[1,2,3];
const dst=[0];
src.forEach((el)=>dst.push(el));

尽管Array.prototype.push返回一个数字,并且Array.prototype.forEach方法期待一个返回void类型的函数,但这段代码依然没有报错,就是因为基于上下文

推导,推导出forEach函数返回类型为void,正是因为不强制函数一定不能返回内容,所有上面这种return dst.push(el)的写法不报错

当一个函数字面量定义返回一个void类型,函数一定不能返回任何东西

1
2
3
4
5
6
7
8
9
function f2(): void {
// @ts-expect-error
return true;
}

const f3 = function (): void {
// @ts-expect-error
return true;
};

具体参考:

https://ts.yayujs.com/handbook/MoreOnFunctions.html#%E5%87%BD%E6%95%B0-more-on-functions

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