您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > javascript

TypeScript 出现 Go 和 Rust的 错误? 没有Try/Catch?

时间:2023-09-07 13:07:22  来源:今日头条  作者:启辰8
JAVAScript 依靠抛出异常来处理错误,而 Go 和 Rust 将它们视为值。 你可能认为这没什么大不了的……但是,孩子,这可能听起来微不足道; 然而,它改变了游戏规则。

那么,让我们从我的一些背景故事开始。 我是一名拥有大约十年经验的软件开发人员,最初使用 php,然后逐渐过渡到 JavaScript

大约五年前,我开始使用 TypeScript,从那时起,我就再也没有回到过 JavaScript。 当我开始使用它的那一刻,我认为它是有史以来最好的编程语言。 每个人都喜欢它; 每个人都用它……这只是最好的,对吧? 正确的? 正确的?

是的,然后我开始尝试其他语言,更现代的语言。 首先是 Go,然后我慢慢地将 Rust 添加到我的列表中(感谢 Prime)。

当您不知道不同事物的存在时,就很难错过事物。

我在说什么? Go 和 Rust 的共同点是什么? 错误。 对我来说最突出的事情。 更具体地说,这些语言如何处理它们。

JavaScript 依靠抛出异常来处理错误,而 Go 和 Rust 将它们视为值。 你可能认为这没什么大不了的……但是,孩子,这可能听起来微不足道; 然而,它改变了游戏规则。

让我们来看看它们。 我们不会深入研究每种语言; 我们想知道一般方法。

让我们从 JavaScript/TypeScript 和一个小游戏开始。

给自己五秒钟的时间来查看下面的代码并回答为什么我们需要将其包装在 try/catch 中。

try {
  const request = { name: “test”, value: 2n };
  const body = JSON.stringify(request);
  const response = awAIt fetch("https://example.com", {
    method: “POST”,
    body,
  });
  if (!response.ok) {
    return;
  }
  // handle response
} catch (e) {
  // handle error
  return;
}

所以,我假设你们大多数人都猜到即使我们正在检查response.ok,fetch 方法仍然会抛出错误。 response.ok 仅“捕获”4xx 和 5xx 网络错误。 但当网络本身出现故障时,就会抛出错误。

但我想知道有多少人猜到 JSON.stringify 也会抛出错误。 原因是请求对象包含bigint(2n)变量,JSON不知道如何字符串化。

所以第一个问题是,就我个人而言,我认为这是有史以来最大的 JavaScript 问题:我们不知道什么会引发错误。 从 JavaScript 错误的角度来看,它与以下内容相同:

try {
  let data = “Hello”;
} catch (err) {
  console.error(err);
}

JavaScript 不知道; JavaScript 不在乎。 你应该知道。

第二件事,这是完全可行的代码:

const request = { name: “test”, value: 2n };
const body = JSON.stringify(request);
const response = await fetch("https://example.com", {
  method: “POST”,
  body,
});
if (!response.ok) {
  return;
}

没有错误,即使这可能会破坏您的应用程序。

现在,在我的脑海中,我可以听到,“有什么问题,只要在任何地方使用 try/catch 就可以了。” 第三个问题来了:我们不知道抛出的是哪一个。 当然,我们可以通过错误消息进行猜测,但是对于有很多可能发生错误的地方的更大的服务/功能呢? 您确定通过一次 try/catch 正确处理了所有这些问题吗?

好吧,是时候停止对 JS 的挑剔,转向其他的事情了。 让我们从这段 Go 代码开始:

f, err := os.Open(“filename.ext”)
if err != nil {
  log.Fatal(err)
}
// do something with the open *File f

我们正在尝试打开一个返回文件或错误的文件。 您会经常看到这种情况,主要是因为我们知道哪些函数总是返回错误。 你永远不会错过任何一个。 这是将错误视为值的第一个示例。 您指定哪个函数可以返回它们,您返回它们,您分配它们,您检查它们,您使用它们。

它也没有那么丰富多彩,这也是 Go 受到批评的事情之一——“错误检查代码”,其中 if err != nil { .... 有时需要比其他代码行更多的代码。

if err != nil {
  …
  if err != nil {
    …
    if err != nil {
      … 
    }
  } 
}
if err != nil {
  … 
}
…
if err != nil {
  … 
}

仍然完全值得付出努力,相信我。

最后,铁锈:

let greeting_file_result = File::open(“hello.txt”);
let greeting_file = match greeting_file_result {
  Ok(file) => file,
  Err(error) => panic!("Problem opening the file: {:?}", error),
};

这里显示的三个中最冗长的一个,具有讽刺意味的是,也是最好的一个。 因此,首先,Rust 使用其令人惊叹的枚举来处理错误(它们与 TypeScript 枚举不同!)。 无需详细介绍,这里重要的是它使用一个名为 Result 的枚举,它有两个变体:Ok 和 Err。 正如您可能猜到的,Ok 保存一个值,Err 保存……令人惊讶的是,一个错误:D。

它还有很多方法可以更方便地处理它们,以缓解 Go 问题。 最知名的是? 操作员。

let greeting_file_result = File::open(“hello.txt”)?;

这里的总结是,Go 和 Rust 总是知道哪里可能出现错误。 它们迫使你在它出现的地方(大部分)处理它。 没有隐藏的,没有猜测,没有令人惊讶的面孔破坏应用程序。

而且这种方法更好。 一英里。

好吧,是时候说实话了; 我撒了一点谎。 我们不能让 TypeScript 错误像 Go / Rust 那样工作。 这里的限制因素是语言本身; 它没有合适的工具来做到这一点。

但我们能做的就是尽量让它相似。 并使其变得简单。

从这个开始:

export type Safe<T> =
  | {
    success: true;
    data: T;
  }
  | {
    success: false;
    error: string;
  };

这里没什么特别的,只是一个简单的泛型类型。 但这个小宝贝可以完全改变代码。 您可能会注意到,这里最大的区别是我们要么返回数据,要么返回错误。 听起来很熟悉?

另外……第二个谎言,我们确实需要一些尝试/捕获。 好消息是我们只需要大约两个,而不是 100,000 个。

export function safe<T>(promise: Promise<T>, err?: string): Promise<Safe<T>>;
export function safe<T>(func: () => T, err?: string): Safe<T>;
export function safe<T>(
  promiseorFunc: Promise<T> | (() => T),
  err?: string,
): Promise<Safe<T>> | Safe<T> {
  if (promiseOrFunc instanceof Promise) {
    return safeAsync(promiseOrFunc, err);
  }
  return safeSync(promiseOrFunc, err);
}

async function safeAsync<T>(
  promise: Promise<T>, 
  err?: string
): Promise<Safe<T>> {
  try {
    const data = await promise;
    return { data, success: true };
  } catch (e) {
    console.error(e);
    if (err !== undefined) {
      return { success: false, error: err };
    }
    if (e instanceof Error) {
      return { success: false, error: e.message };
    }
    return { success: false, error: "Something went wrong" };
  }
}

function safeSync<T>(
  func: () => T, 
  err?: string
): Safe<T> {
  try {
    const data = func();
    return { data, success: true };
  } catch (e) {
    console.error(e);
    if (err !== undefined) {
      return { success: false, error: err };
    }
    if (e instanceof Error) {
      return { success: false, error: e.message };
    }
    return { success: false, error: "Something went wrong" };
  }
}

“哇哦,真是个天才。 他为 try/catch 创建了一个包装器。” 是的你是对的; 这只是一个包装器,以我们的 Safe 类型作为返回类型。 但有时您所需要的只是简单的事情。 让我们将它们与上面的示例结合起来。

旧的(16行):

try {
  const request = { name: “test”, value: 2n };
  const body = JSON.stringify(request);
  const response = await fetch("https://example.com", {
    method: “POST”,
    body,
  });
  if (!response.ok) {
    // handle.NETwork error
    return;
  }
  // handle response
} catch (e) {
  // handle error
  return;
}

新的(20行):

const request = { name: “test”, value: 2n };
const body = safe(
  () => JSON.stringify(request),
  “Failed to serialize request”,
);
if (!body.success) {
  // handle error (body.error)
  return;
}
const response = await safe(
  fetch("https://example.com", {
    method: “POST”,
    body: body.data,
  }),
);
if (!response.success) {
  // handle error (response.error)
  return;
}
if (!response.data.ok) {
  // handle network error
  return;
}
// handle response (body.data)

所以,是的,我们的新解决方案更长,但性能更好,原因如下:

  • 没有try/catch。
  • 我们处理发生的每个错误。
  • 我们可以为特定函数指定错误消息。
  • 我们有一个很好的从上到下的逻辑,所有错误都在顶部,然后只有响应在底部。

但现在王牌来了。 如果我们忘记检查这一点会发生什么:

if (!body.success) {
  // handle error (body.error)
  return;
}

问题是……我们不能。 是的,我们必须进行这项检查。 如果不这样做,body.data 将不存在。 LSP 将通过抛出“‘Safe<string>’类型上不存在属性‘data’”错误来提醒我们。 这一切都归功于我们创建的简单 Safe 类型。 它也适用于错误消息。 在检查 !body.success 之前,我们无法访问 body.error。

现在我们应该欣赏 TypeScript 以及它如何改变 JavaScript 世界。

以下内容也是如此:

if (!response.success) {
  // handle error (response.error)
  return;
}

我们不能删除 !response.success 检查,因为否则,response.data 将不存在。

当然,我们的解决方案并非没有问题。 最重要的一点是,您必须记住使用我们的安全包装器来包装可能引发错误的 Promise/函数。 这种“我们需要知道”是我们无法克服的语言限制。

听起来可能很难,但事实并非如此。 您很快就会开始意识到,代码中几乎所有的 Promise 都可能会抛出错误,而同步函数也会抛出错误,您知道它们,但它们并不多。

不过,您可能会问,值得吗? 我们认为是的,而且它在我们的团队中运行得很好:)。 当您查看更大的服务文件时,任何地方都没有 try/catch,每个错误都在出现的地方进行处理,具有良好的逻辑流程……它看起来不错。

以下是使用 SvelteKit Formaction 的真实示例:

export const actions = {
  createEmail: async ({ locals, request }) => {
    const end = perf(“CreateEmail”);
    const form = await safe(request.formData());
    if (!form.success) {
      return fail(400, { error: form.error });
    }
    const schema = z
      .object({
        emailTo: z.string().email(),
        emailName: z.string().min(1),
        emailSubject: z.string().min(1),
        emailhtml: z.string().min(1),
      })
    .safeParse({
      emailTo: form.data.get("emailTo"),
      emailName: form.data.get("emailName"),
      emailSubject: form.data.get("emailSubject"),
      emailHtml: form.data.get("emailHtml"),
    });
    if (!schema.success) {
      console.error(schema.error.flatten());
      return fail(400, { form: schema.error.flatten().fieldErrors });
    }
    const metadata = createMetadata(URI_GRPC, locals.user.key)
    if (!metadata.success) {
      return fail(400, { error: metadata.error });
    }
    const response = await new Promise<Safe<Email__Output>>((res) => {
      usersClient.createEmail(schema.data, metadata.data, grpcSafe(res));
    });
    if (!response.success) {
      return fail(400, { error: response.error });
    }
    end();
    return {
      email: response.data,
    };
  },
} satisfies Actions;

这里有几点需要指出:

  • 我们的自定义函数 grpcSafe 帮助我们处理 gGRPC 回调。
  • createMetadata 在内部返回 Safe,所以我们不需要包装它。
  • zod 库使用相同的模式:) 如果我们不进行 schema.success 检查,我们就无法访问 schema.data。

是不是看起来很干净呢? 所以尝试一下吧! 也许它也非常适合您:)

谢谢阅读。

附: 看起来很相似?

f, err := os.Open(“filename.ext”)
if err != nil {
  log.Fatal(err)
}
// do something with the open *File f
 
const response = await safe(fetch(“https://example.com"));
if (!response.success) {
  console.error(response.error);
  return;
}
// do something with the response.data


Tags:TypeScript   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
一篇文章搞懂TypeScript
TypeScript 是 JavaScript 的超集,一方面给动态类型的 js 增加了类型校验,另一方面扩展了 js 的各种功能。原始数据类型 字符串 数值 布尔 null undefined Symbol BigIntlet s...【详细内容】
2024-01-08  Search: TypeScript  点击:(77)  评论:(0)  加入收藏
TypeScript中的null和undefined的区别
在TypeScript中,null和undefined是两个特殊的值,用于表示变量的缺失或未定义。尽管它们在某些情况下可能看起来相似,并且都可以表示"没有值",但它们在语义和用法上存在一些重要...【详细内容】
2023-12-07  Search: TypeScript  点击:(132)  评论:(0)  加入收藏
为什么要在项目中使用TypeScript?
译者 | 李睿随着越来越多的开发人员采用TypeScript,人们需要了解在下一个项目中应该使用TypeScript的原因。尽管它在早期应用中遇到了一些阻力,但在过去十年,它迅速成为一种广...【详细内容】
2023-11-30  Search: TypeScript  点击:(162)  评论:(0)  加入收藏
一文读懂 TypeScript 泛型及应用
泛型是静态类型语言的基本特征,允许将类型作为参数传递给另一个类型、函数、或者其他结构。TypeScript 支持泛型作为将类型安全引入组件的一种方式。这些组件接受参数和返回...【详细内容】
2023-11-20  Search: TypeScript  点击:(194)  评论:(0)  加入收藏
TypeScript 5.3 来了,一大波新特性
根据 TypeScript 路线图,TypeScript 5.3 计划于 11 月 14 日发布。下面是该版本带来的新特性: 导入属性 导入类型中稳定支持 resolution-mode 所有模块模式均支持 resolution-...【详细内容】
2023-11-16  Search: TypeScript  点击:(153)  评论:(0)  加入收藏
万字详解 TypeScript 高级用法
TypeScript 是一种类型安全的 JavaScript 超集,除了基本类型和对象类型之外,TypeScript 还提供了一些高级类型系统,使得我们可以更好地处理复杂的数据结构和业务逻辑。本文将深...【详细内容】
2023-10-31  Search: TypeScript  点击:(339)  评论:(0)  加入收藏
不要在Typescript中使用Function类型
原文链接:https://www.totaltypescript.com/dont-use-function-keyword-in-typescript翻译:一川在Typescript中不应该使用Function作为一个类型,因为它可以表示任何函数。通常,...【详细内容】
2023-09-27  Search: TypeScript  点击:(279)  评论:(0)  加入收藏
为什么选择 TypeScript,它有什么优点吗?
在当今快速发展的软件开发领域,TypeScript技术的重要性日益凸显。TypeScript是一种由微软开发的开源编程语言,它扩展了JavaScript,并为开发者提供了强大的静态类型检查。首先,Ty...【详细内容】
2023-09-22  Search: TypeScript  点击:(191)  评论:(0)  加入收藏
掌握TypeScript,开启高质量前端之旅
相信TypeScript对于一个前端开发来讲应该是不陌生的,因为作为一个前端开发者来说,不是在学习如何使用TS就是在去学习TS如何使用的路上。为什么这么说呢?一些技术的发展离不开人...【详细内容】
2023-09-22  Search: TypeScript  点击:(283)  评论:(0)  加入收藏
反驳来了!放弃TypeScript?说明你无知!
作者 | Robert Vitonsky编译 | 云中 几天前,汉森 (David Heinemeier Hansson)宣布 Turbo8 即将放弃 TypeScript。我心想:放弃就放弃吧,反正我也不知道 Turbo 8 是什么鬼。 然而...【详细内容】
2023-09-15  Search: TypeScript  点击:(232)  评论:(0)  加入收藏
▌简易百科推荐
17 个你需要知道的 JavaScript 优化技巧
你可能一直在使用JavaScript搞开发,但很多时候你可能对它提供的最新功能并不感冒,尽管这些功能在无需编写额外代码的情况下就可以解决你的问题。作为前端开发人员,我们必须了解...【详细内容】
2024-04-03  前端新世界  微信公众号  Tags:JavaScript   点击:(4)  评论:(0)  加入收藏
你不可不知的 15 个 JavaScript 小贴士
在掌握如何编写JavaScript代码之后,那么就进阶到实践&mdash;&mdash;如何真正地解决问题。我们需要更改JS代码使其更简单、更易于阅读,因为这样的程序更易于团队成员之间紧密协...【详细内容】
2024-03-21  前端新世界  微信公众号  Tags:JavaScript   点击:(25)  评论:(0)  加入收藏
又出新JS运行时了!JS运行时大盘点
Node.js是基于Google V8引擎的JavaScript运行时,以非阻塞I/O和事件驱动架构为特色,实现全栈开发。它跨平台且拥有丰富的生态系统,但也面临安全性、TypeScript支持和性能等挑战...【详细内容】
2024-03-21  前端充电宝  微信公众号  Tags:JS   点击:(23)  评论:(0)  加入收藏
构建一个通用灵活的JavaScript插件系统?看完你也会!
在软件开发中,插件系统为应用程序提供了巨大的灵活性和可扩展性。它们允许开发者在不修改核心代码的情况下扩展和定制应用程序的功能。本文将详细介绍如何构建一个灵活的Java...【详细内容】
2024-03-20  前端历险记  微信公众号  Tags:JavaScript   点击:(20)  评论:(0)  加入收藏
对JavaScript代码压缩有什么好处?
对JavaScript代码进行压缩主要带来以下好处: 减小文件大小:通过移除代码中的空白符、换行符、注释,以及缩短变量名等方式,可以显著减小JavaScript文件的大小。这有助于减少网页...【详细内容】
2024-03-13  WangLiwen    Tags:JavaScript   点击:(2)  评论:(0)  加入收藏
跨端轻量JavaScript引擎的实现与探索
一、JavaScript 1.JavaScript语言JavaScript是ECMAScript的实现,由ECMA 39(欧洲计算机制造商协会39号技术委员会)负责制定ECMAScript标准。ECMAScript发展史: 2.JavaScript...【详细内容】
2024-03-12  京东云开发者    Tags:JavaScript   点击:(2)  评论:(0)  加入收藏
面向AI工程的五大JavaScript工具
令许多人惊讶的是,一向在Web开发领域中大放异彩的JavaScript在开发使用大语言模型(LLM)的应用程序方面同样大有价值。我们在本文中将介绍面向AI工程的五大工具,并为希望将LLM...【详细内容】
2024-02-06    51CTO  Tags:JavaScript   点击:(52)  评论:(0)  加入收藏
JS小知识,使用这6个小技巧,避免过多的使用 if 语句
最近在重构我的代码时,我注意到早期的代码使用了太多的 if 语句,达到了我以前从未见过的程度。这就是为什么我认为分享这些可以帮助我们避免使用过多 if 语句的简单技巧很重要...【详细内容】
2024-01-30  前端达人  今日头条  Tags:JS   点击:(56)  评论:(0)  加入收藏
18个JavaScript技巧:编写简洁高效的代码
本文翻译自 18 JavaScript Tips : You Should Know for Clean and Efficient Code,作者:Shefali, 略有删改。在这篇文章中,我将分享18个JavaScript技巧,以及一些你应该知道的示例...【详细内容】
2024-01-30  南城大前端  微信公众号  Tags:JavaScript   点击:(65)  评论:(0)  加入收藏
使用 JavaScript 清理我的 200GB iCloud,有了一个意外发现!
本文作者在综合成本因素之下,决定用 Java 脚本来清理一下自己的 iCloud,结果却有了一个意外发现,即在 iCloud 中上传同一个视频和删除此视频之后,iCloud 的空间并不一致,这到底是...【详细内容】
2024-01-11    CSDN  Tags:JavaScript   点击:(97)  评论:(0)  加入收藏
站内最新
站内热门
站内头条