介绍
打字稿是JavaScript发生的最好的事情,因为很棒的回调金字塔崩溃了。
我们有类型的类型,自动完成,编译时间问题,世界终于处于和平状态,直到某人决定使用instanceof
(是的,是我)。
您看到的,即使我们得到了上述所有的好东西,仍然有一些打字稿类型系统的怪癖可能会在雷达下飞行,因为它们经常出现,尤其是当您编写相对简单的应用程序时。
在本文中,我想深入研究对象的主题。具体而言,可以实例化对象的不同方式,它们为何重要以及如何使用 /滥用instanceof
运算符。< / p>
我会以一个有关错误处理的实践示例来支持所有内容,并向您展示如何获得打字稿的结构化打字系统的灵活性,并具有运行时对象类型检查的功能。
示例用例
您是前端开发人员,您被要求实现应用程序的错误处理。对于此应用程序,假设您需要处理的错误类型为:
- 身份验证错误
- 后端错误
- 任何不适合前两种类型的未接收错误。
让我们看看如何为此实施一些东西。
1.如果我们只使用打字稿怎么办?
就像一个好的开发人员一样,我们通过为每个错误创建一种类型来开始简单:
type AuthError = { reason: string };
type BackendError = { reason: string };
type UnknownError = { reason: string };
就在大门时,我们有一些主要问题:
- 对于打字稿,这些都是the same thing
- 到JavaScript,这些甚至不存在
- 代码重复
让我们继续前进。
2.如果我们使用课程怎么办?
好主意!毕竟,在解决第二期的运行时也可以上课。
class AuthError {
constructor(public reason: string) {}
}
class BackendError {
constructor(public reason: string) {}
}
class UnknownError {
constructor(public reason: string) {}
}
我们仍然有两个问题。对于打字稿来说,这些仍然是同一件事,我们仍在重复自己。
代码重复问题足够简单,无法处理。我们可以做一个超级阶级,并让其他人扩展它:
class CustomError {
constructor(public reason: string) {}
}
class AuthError extends CustomError {}
class BackendError extends CustomError {}
class UnknownError extends CustomError {}
上面的代码是完全有效的。尽管打字稿无法分辨出差异,但也许我们不需要它!毕竟,我们可以使用instanceof
,以下代码效果很好:
function handleError(error: CustomError) {
if (error instanceof AuthError) {
// do something
} else if (error instanceof BackendError) {
// do something
} else if (error instanceof UnknownError) {
// do something
}
}
太好了!现在,我们照顾了我们之前提到的所有问题,但是文章尚未完成。哦,哦。即将在JavaScript中改变您对某些事物的看法。
这个问题隐藏在instanceof
的工作原理中。
用最简单的话来说,如果someObject
是由SomeClass
构造器创建或继承的,则someObject instanceof SomeClass
将返回True,这意味着他们的某个地方必须有一个new
关键字。大事,那呢?我们只会像这样提出错误:
throw new AuthError('Session is expired or something');
好的。足够公平,除了这是JavaScript的狂野未驯服的土地,您也可以在这里这样做:
throw { reason: 'Session is expired or something' };
零警告。零错误。但这甚至不是最糟糕的部分。您刚刚失去了在运行时检查此问题的能力:
const errorObj: AuthError = { reason: 'Session is expired or something' };
console.log(errorObj instanceof AuthError); // -> False
instanceof
不使用new
关键字的实例化对象时不起作用。
尽管这是一个不太可能的问题,但我个人对instanceof
失去了信心。每当在代码的这一区域中发生问题时,我脑海中的一点声音都会不断告诉我“如果是因为您一次读过的那个罕见的错误怎么办?”
我们需要一种不同的方法。一个使我们从JavaScript的怪癖中拯救出来,并允许打字稿真正发光。
3.再次回到类型
引入标签!标签只是一个具有恒定值的属性,该属性放置在类型中,使我们可以在编译时间和运行时自信地缩小给定对象的类型。
这是我们现在可以定义错误的方法:
type AuthError = {
reason: string,
errorType: 'auth-error', // <- tag
};
type BackendError = {
reason: string,
errorType: 'backend-error', // <- tag
};
type UnknownError = {
reason: string,
errorType: 'unknown-error', // <- tag
};
type CustomError = AuthError | BackendError | UnknownError;
有趣的是,CustomError
现在位于我们的层次结构的底部,而不是顶部。使用|
(Union)操作员,我们告诉Typescript,CustomError
是任何给定类型之一。我们能够缩小使用标签的范围。
以下是可以重新编写handleError
函数的方式:
function handleError(error: CustomError) {
switch (error.errorType) {
case 'auth-error':
// ...
case 'backend-error':
// ...
default:
// ...
}
}
最酷的部分? Typescript正在帮助您沿着每个步骤,并完全了解您要处理的错误类型。
请注意,要为此,标签需要具有所有类型的同名(上面的示例中的errorType
)。
如果您对如何以这种方式打印类型感兴趣(使用// ^?
)查看this extension(我没有附属,请觉得很棒)。
现在,即使我们将其生育并直接扔对象也无关紧要。打字稿会发疯,迫使我们添加一个标签:
// ERROR: Property 'errorType' is missing in type '{ reason: string; }' but required in type 'AuthError'.
const errorObject: AuthError = {
reason: string;
};
throw errorObject;
结论
总而言之,本文强调了使用错误处理示例解释的Typescript和JavaScript类型系统的重要性。
通过逐步探索,我们发现使用标签是一个强大而可靠的解决方案。此方法利用了Typescript的结构化键入系统,并允许运行时对象类型检查,从而产生更健壮和可维护的代码。
通过掌握这些细微差别,我们可以在避免语言陷阱的同时充分利用打字稿的潜力。
,要对Typescript进行更多了解,我强烈建议Dan Vanderkam的有效打字稿。