TypeScript 支持
Sutando 提供了 TypeScript 支持,但我们采取了实用主义的方式 - 在易用性和类型安全性之间取得平衡。我们更注重提供直观、易用的 API,而不是追求完全的类型安全。
基本用法
以下是一些基本示例:
typescript
import { Model } from 'sutando'
// 定义一个基本的模型
class User extends Model {
// 可选:声明模型属性类型
declare id: number
declare name: string
declare email: string
}
// 使用模型
const user = new User()
user.name = 'John'
await user.save()
// 查询示例
const users = await User.query()
.where('age', '>', 18)
.get()
// 关联关系示例
class Post extends Model {
declare title: string
declare content: string
declare user_id: number
relationUser() {
return this.belongsTo(User)
}
}
类型安全说明
虽然 Sutando 提供了 TypeScript 支持,但我们并不追求完全的类型安全。这意味着:
- 某些动态特性可能无法获得完整的类型推导
- 查询构建器的某些操作可能返回
any
类型 - 关联关系的类型推导可能不够完善
例如:
typescript
// 这样的动态查询可能无法获得准确的类型推导
const result = await User.query()
.select(['name', 'email'])
.where('age', '>', 18)
.first()
// 关联关系查询的类型推导可能不够完善
const userWithPosts = await User.query()
.with('posts')
.first()
使用泛型增强类型安全
为了解决上述类型推导的限制,Sutando 的查询方法支持泛型类型,这让你可以:
- 扩展模型的类型定义
- 指定关联数据的类型
- 添加自定义字段的类型
例如:
typescript
// 基础查询使用泛型
const user = await User.query()
.first<User & {
computed_field: string;
}>()
// 关联查询使用泛型
const post = await Post.query()
.with('user')
.first<Post & {
user: User;
}>()
// 自定义查询结果类型
interface CustomUserResult extends User {
total_posts: number;
latest_login: Date;
}
const result = await User.query()
.select(['*'])
.selectRaw('COUNT(posts.id) as total_posts')
.first<CustomUserResult>()
// 复杂的关联查询类型
const userWithPosts = await User.query()
.with('posts')
.first<CustomUserResult & {
posts: Post[];
}>()
为什么这样设计?
我们的设计理念是:
- 优先考虑开发体验:我们希望 API 保持简单直观,而不是被复杂的类型定义所困扰
- 实用性优先:在某些场景下,我们选择牺牲一定的类型安全性来换取更灵活的 API
- 渐进式类型支持:你可以根据需要逐步添加更多类型定义
最佳实践
尽管如此,我们仍然建议:
- 为模型的主要属性声明类型
- 对关键的业务逻辑代码添加类型注解
- 在需要类型安全的地方使用类型断言或自定义类型守卫
typescript
// 为重要的模型属性声明类型
class Product extends Model {
declare id: number
declare name: string
declare price: number
declare stock: number
// 自定义方法使用明确的类型
async updateStock(quantity: number): Promise<void> {
this.stock += quantity
await this.save()
}
}