TypeScript小技巧(二)
点击查看上一篇
使用using关键字
如数据库、文件等IO相关的API,一般需要在合适的时机将资源释放,经常需要写这样的代码:
try {
db.xxx();
} catch (e) {
// handle error
} finally {
db.close();
}
在这个TC39提案里,引入了一个新的关键字using
,用于简化这个流程,当然现在还没有什么浏览器支持这个特性,而TypeScript 5.2对这个关键字提供了支持(tsc会把它转换成旧语法)。首先需要有一个暴露[Symbol.dispose]
方法的对象,然后在一个代码块中使用using代替const初始化这个对象。
function createDb() {
console.log("Open!")
return {
fetch: (params: string) => {
console.log(params)
},
[Symbol.dispose]: () => {
console.log("Close!")
}
}
}
{
using db = createDb()
db.fetch("select * from table")
}
最后当离开作用域时,该对象的[Symbol.dispose]
方法就会被自动调用了。
它还有个对应的async版本,需要这么写:
function createAsyncDb() {
console.log("Open!")
return {
fetch: async (params: string) => {
console.log(params)
},
[Symbol.asyncDispose]: async () => {
console.log("Close!")
}
}
}
{
await using db = createAsyncDb()
await db.fetch("select * from table")
}
不过我是不认同为了省去一个手动close的操作,引入一个新关键字的,相比之下,Kotlin的use方式我认为更好。
satisfies
有时会遇到一种情况,如果一个属性的类型是和类型,它的字面量默认不会被推断为字面的类型:
type Foo = Record<string, string | number | undefined>
const foo: Foo = {
a: "nice"
}
// 这里就会收到类型错误,因为tsc不认为a是字符串类型
// foo.a不被看作string类型,而是string | number | undefined
foo.a.startsWith("n")
如果改写成:
const foo = {
a: "nice"
} satisfies Foo
就不会再有错误提示了。使用satisfies
的另一个区别是,它会限制对象初始化后不能拓展属性:
const foo = {
hello: "nice"
} satisfies Foo
// 错误
foo.past = 1
const bar: Foo = {
hello: "nice"
}
// 没问题
bar.past = 1
同时它还可以和as const
一起用:
const foo = {
hello: "nice",
bar: 3,
} as const satisfies Foo
// Cannot assign to 'hello' because it is a read-only property.
foo.hello = 1