前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >TypeScript 的魔法技能:satisfies

TypeScript 的魔法技能:satisfies

作者头像
前端修罗场
发布2023-10-07 19:55:59
3370
发布2023-10-07 19:55:59
举报
文章被收录于专栏:Web 技术Web 技术

现在,随着 TS 4.9 的发布,在 TypeScript 中有了一种新的、更好的方式来做类型安全校验。它就是 satisfies

代码语言:javascript
复制
type Route = { path: string; children?: Routes }
type Routes = Record<string, Route>

const routes = {
  AUTH: {
    path: "/auth",
  },
} satisfies Routes; 

为什么是 satisfies

在上面的示例中,我给出了 satisfies 的使用示例,但是我并没有解释那样做的原因。现在,是该给你解释解释了。

让我们从使用 TS 的标准类型声明重写上面的示例来进行一个对比:

代码语言:javascript
复制
type Route = { path: string; children?: Routes }
type Routes = Record<string, Route>

const routes: Routes = {
  AUTH: {
    path: "/auth",
  },
}

这看起来似乎没有什么呀,很正常,IDE 也会自动帮我们进行自动补齐。

但是,当我们使用 routes 对象时,因为 IDE 并不知道实际配置的路由是什么。

例如,下面这行代码编译得很好,但会在运行时会抛出错误:

代码语言:javascript
复制
routes.NONSENSE.path // TypeScript 报错:发现这个路由属性不存在

为什么会这样? 这是因为我们的 Routes 类型可以接受任何字符串作为键。所以TypeScript 批准任何键访问,包括从简单的错别字到完全没有意义的键。

有同学会说:“那么用 as 关键字来解决不行吗” 。 很好的问题,我们接着看下面这段代码,用 as 会起到什么效果:

代码语言:javascript
复制
type Route = { path: string; children?: Routes }
type Routes = Record<string, Route>

const routes = {
  AUTH: {
    path: "/auth",
  },
} as Routes

这是 TS 中常见的做法,但实际上是相当危险的。

因为我们不仅会遇到和上面一样的问题,而且你会写出完全不存在的键值对,因为 TypeScript 会以另一种方式看待这样的写法:

代码语言:javascript
复制
type Route = { path: string; children?: Routes }
type Routes = Record<string, Route>

const routes = {
  AUTH: {
    path: "/auth",
    nonsense: true,// TS 可以编译,但这不是一个有效的属性
  },
} as Routes

一般来说,你应该尽量避免在 TypeScript 中使用 as 关键字。

Satisfies

现在,我们再使用 satisfies 关键字重写上面的例子看看:

代码语言:javascript
复制
type Route = { path: string; children?: Routes }
type Routes = Record<string, Route>

const routes = {
  AUTH: {
    path: "/auth",
  },
} satisfies Routes

有了这个,我们会得到了我们想要的所有正确的类型检查:

代码语言:javascript
复制
routes.AUTH.path     // ?
routes.AUTH.children // ? routes.auth has no property `children`
routes.NONSENSE.path // ? routes.NONSENSE doesn't exist

同时,在 IDE 中还能进行自动补全功能:

在这里插入图片描述
在这里插入图片描述

我们再举一个稍微复杂一点的例子,进一步理解:

代码语言:javascript
复制
type Route = { path: string; children?: Routes }
type Routes = Record<string, Route>

const routes = {
  AUTH: {
    path: "/auth",
    children: {
      LOGIN: {
        path: '/login'
      }
    }
  },
  HOME: {
    path: '/'
  }
} satisfies Routes

我们从下图中看到,IDE 自还是能够帮助你进行自动补全和类型检查,一直精确到你的 routes 的叶子属性:

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
routes.AUTH.path                // ?
routes.AUTH.children.LOGIN.path // ?
routes.HOME.children.LOGIN.path // ? routes.HOME has no property `children`

与 as const 结合

当然,在开发中你还可能遇到的一种情况是,仅使用简单的 satisfies 关键字,我们对对象的捕获比理想的情况要松散一些。

例如,下面的代码中,

代码语言:javascript
复制
const routes = {
  HOME: { path: '/' }
} satisfies Routes

如果我们检查 path 属性的类型,我们会得到字符串类型:

代码语言:javascript
复制
routes.HOME.path // Type: string

但是当涉及到配置时, const 断言(又名 as const)真正发光的作用便来了。我们在这里使用 as const,我们会得到更精确的类型,精确到字符串的字面量 '/':

代码语言:javascript
复制
const routes = {
  HOME: { path: '/' }
} as const

routes.HOME.path // Type: '/'

那这么做的理由是什么吗?我平时很少遇到这样的情况。 那我想所得是,假设你有一个这样的方法,它一直是类型安全的,它接受的确切 path:

代码语言:javascript
复制
function navigate(path: '/' | '/auth') { ... }

如果我们只使用 satisfies,其中每个 path 只知道是一个 string,那么 TS 会在报类型错误:

代码语言:javascript
复制
const routes = {
  HOME: { path: '/' }
} satisfies Routes

navigate(routes.HOME.path) 
// ? Argument of type 'string' is not assignable to parameter of type '"/" | "/auth"'

因为 Home.path 是一个有效的字符串 ('/'),但是 TypeScript 说它不是。

那么,这种情况下,我们可以通过组合 satisfiesas const 得到最好的结果:

代码语言:javascript
复制
  const routes = {
    HOME: { path: '/' }
- } satisfies Routes
+ } as const satisfies Routes

现在,我们有了一个很好的解决方案,通过类型检查一直到我们使用的确切的字面量值。

代码语言:javascript
复制
const routes = {
  HOME: { path: '/' }
} as const satisfies Routes 

navigate(routes.HOME.path) // ? - as desired
navigate('/invalid-path')  // ? - as desired

最后,你可能会问,为什么不直接使用 as const 呢?

对于 as const在创建对象时,我们不会对对象本身进行任何类型检查。因此,这意味着在我们的 IDE 中没有自动检查,也没有在编写时对错别字和其他问题的警告。

这就是为什么要进行组合的原因。

Typescript 4.9 引入了新的 satisfies 关键字,它对于 Typescript 中大多数与类型检查、匹配相关的任务都非常方便。

与标准类型声明相比,它可以在类型检查和理解匹配的细节之间取得优雅的平衡,以获得最佳类型安全性。还没用上的同学,还去试试吧~

本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么是 satisfies
  • Satisfies
  • 与 as const 结合
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com