聊一聊Typescript中与this相关的类型定义
forthealllight opened this issue · 0 comments
聊一聊Typescript中与this相关的类型定义
从本文开始,会陆续介绍一些typescript的使用,由浅入深,本文主要介绍一下Typescript中的this用法以及与this相关的内置函数。
- Typescript的函数中声明this
- Typescript中箭头函数和this声明
- Typescript与this相关的内置函数
一、Typescript的函数中声明this
(1)函数中的this声明
为了说明在typescript中this的声明方法,我们首先需要更改tsconfig.json的配置:
"compilerOptions": {
...
"noImplicitThis": true
}
默认情况下,如果ts没有this对象类型声明,this是自动隐式定义。如果noImplicitThis设置为true,此时不允许this上下文隐式定义,如果使用了没有声明过的this对象就会报错,举例来说(当设置noImplicitThis:true):
function test() {
const { a } = this;
console.log(a);
}
test.call({ a: 1 });; //err 'this' implicitly has type 'any' because it does not have a type annotation. TS2683
本文主要是为了介绍如何在typescript中声明this,因此需要将noImplicitThis设置为true,不允许隐式定义this,必须显式声明this。
简单来说,默认情况下可以理解成typescript将函数中的this as any,而oImplicitThis:true的情况下,必须去声明this的类型,才能在函数或者对象中使用this.
我们来看一个简单的this的声明:
function test(this:{a:number}}) {
const { a } = this;
console.log(a);
}
test.call({ a: 1 });;
上述我们在test函数的第一个参数中声明了this的类型,就不会报错。值得注意的是,this必须声明在函数参数声明中的第一个,且this在函数参数中的声明,不作为形参和实参,因此一下两种方式都是错误的:
- this没有在函数第一个参数中声明
function test(b: number, this: { a: number }) {
const {a} = this;
console.log(a);
}
test.call({ a: 1 });// error A 'this' parameter must be the first parameter. TS2680
- 错误的调用了this声明的函数
function test(this: { a: number }) {
const {a} = this;
console.log(a);
}
test({ a: 1 }); // 直接以传参数的形式调用test,Expected 0 arguments, but got 1. TS2554
(2)回调函数中this声明
除了在函数参数中可以定义this的类型外,在回调函数中也可以声明回调函数中的this的类型,这个听起来比较拗口,我们直接来看例子:
interface UIElement {
addClickListener(onclick: (this: void, e: Event) => void): void;
}
class Handler {
info: string;
onClickBad(this: Handler, e: Event) {
// oops, used `this` here. using this callback would crash at runtime
this.info = e.message;
}
}
const uiElement: UIElement = {
addClickListener: () => {},
};
const h = new Handler('hello');
uiElement.addClickListener(h.onClickBad); // error Argument of type '(this: Handler, e: Event) => void' is not assignable to parameter of type '(this: void, e: Event) => void'.
The 'this' types of each signature are incompatible.
Type 'void' is not assignable to type 'Handler'. TS2345
上述的例子中,在uiElement.addClickListener(h.onClickBad)中会报错,因为uiElement.addClickListener接受回调函数作为参数,该回调函数中的this的定义是void,而h.onClickBad这个函数真实的this定义是Handler。
void != Handler
因此会报错。
这种情况,我们只需要修改addClickListener接受回调参数的this定义即可:
interface UIElement {
addClickListener(onclick: (this: Handle, e: Event) => void): void;
}
当然修改h.onClickBad中的this定义也能达到相同的效果。
二、Typescript中箭头函数和this声明
抛开this声明,仅仅考虑this的话,typescript中箭头函数的this和ES6中箭头函数中的this是一致的。
箭头函数中的this,指向定义该函数时的那个对象。
这里顺便补充一下什么是箭头函数中定义时绑定this对象。我们来举两个例子:
(1)、
var x=11;
var obj={
x:22,
say:function(){
console.log(this.x)
}
}
obj.say();
//console.log输出的是22
var x=11;
var obj={
x:22,
say:()=>{
console.log(this.x);
}
}
obj.say();
//输出11
这是最简单的说明箭头函数中this的例子,箭头函数中的this,指向定义该函数时的执行上下文,在obj中定义的say方法,在定义的say方法的时候,其所在执行上下文为window,因此在箭头函数的例子中输出this.x === window.x,因此是11.
(2)、再来看一个复杂的例子:
var a=11
function test1(){
this.a=22;
let b=function(){
console.log(this.a);
};
b();
}
var x=new test1();
//输出11
var a=11;
function test2(){
this.a=22;
let b=()=>{console.log(this.a)}
b();
}
var x=new test2();
//输出 22
上面的例子,不一一说明。总之只要记住在箭头函数中this,在定义的时候绑定。
typescript规定,箭头函数不需要声明this的类型。
function test(this: { a: number }) {
const b = () => {
console.log(this);
};
b();
}
test.call({ a: 6 }); // 输出this为{a:6}
function test(this: { a: number }) {
const b = (this:{a:number}) => {
console.log(this);
};
b();
}
test.call({ a: 6 }); // ts error An arrow function cannot have a 'this' parameter
因此如果我们不想对于每一个带this的函数,都一一去声明this的类型,那么用箭头函数来代替普通函数,就可以省去声明this类型的过程。
三、Typescript与this相关的内置函数
(1)、ThisParameterType
ThisParameter接受一个Type作为范型函数,这个Type必须是函数的类型,最后返回的是该函数中this的类型。这句话听起来很拗口,接着来看例子:
function test(this: number) {}
const n: ThisParameterType<typeof test> = 1;
//不会报错 ThisParameterType<typeof test> 最后的类型就是number
上述中typeof test就是一个函数的类型,然后因为test函数中this声明的类型为number,因此通过
ThisParameterType<typeof test>
拿到就是test函数中this声明的这个number类型。
(2)、OmitThisParameter
OmitThisParameter是移除type类型中this声明的类型 。
function test(this: number) {}
const t: OmitThisParameter<typeof test> = test.bind(1);
t();
上述
OmitThisParameter<typeof test>
类型就是:()=> void
(3)、ThisType
ThisType较为晦涩,ThisType一般用于字面量对象中,用于定义字面量对象的this。
比如:
interface Point {
x: number;
y: number;
}
const myCustomObj: ThisType<Point> = {
moveRight() {
this.x++;
},
moveLeft() {
this.x++;
}
}
这里myCustomObj这个对象字面量类型中的this为Point,因此在myCustomObj这个对象内部使用this.x是类型安全的。
此外thisType通常用于对象的组合和扩展中,来看一个更复杂的例子:
type BasicPoint = {
x: number;
y: number;
}
function makeCustomPoint(methods: ThisType<BasicPoint>) {
return { x: 0, y: 0, ...methods };
}
const customPoint = makeCustomPoint({
moveRightBy: function (xTranslate: number) {
// "this.x" is a number
this.x += xTranslate;
}
});