我一直认为API调用的结果在TypeScript中是这样的:
interface Res {
films: string;
people: string;
}
const res: Res = await fetch("https://swapi.dev/api/")
.then((res) => {
return res.json();
})
.then(({ films, people }) => {
return {
films,
people,
};
});
后来我添加了运行时类型检查与Zod
const resSchema = z.object({
films: z.string(),
people: z.string(),
});
resSchema.parse(res);
这可以工作,但这意味着我有两个不同的类型定义要维护。在TypeScript中是否有一种方法可以检查resSchema
的类型是否等于Res
?
我知道zod可以推断:
type ResType = z.infer<typeof resSchema>
const res: ResType = await fetch("https://swapi.dev/api/")
.then((res) => {
return res.json();
})
.then(({ films, people }) => {
return {
films,
people,
};
});
然而,我不想使用这个,因为我不能一眼就看到ResType
上的字段与Res
相比,这是非常清楚的。
可以通过断言模式的类型而不是从模式推断类型来实现这一点。在您的示例中,如果您已经有了Res
接口,您可以将schema
定义为:
import { z } from "zod";
interface Res {
films: string;
people: string;
}
const schema: z.ZodType<Res> = z.object({
films: z.string(),
people: z.string(),
});
如果你更新Res
,你会在你定义schema
的那一行得到一个类型错误,即z.object
的值不能再分配给你指定的ZodType
。
这也有一个很好的属性,即schema.parse
的返回值也将在编辑器中显示为Res
。如果您给接口一个有意义的名称和javadoc风格的注释,它们将被授予编辑器语言服务器中parse
的返回值。
打印稿操场
虽然@Souperman的答案有效,但它不考虑null
,undefined
或可选属性(?
)。
所以我开始寻找一个解决方案,可以…
解决方案1:
这将说明null
,undefined
或可选属性(?
)。
它使用"tozod"包中。查看这里的github和npm。
你可以这样使用:
import { z } from "zod";
import { toZod } from "tozod";
interface Res {
films?: string; // Note that "films" is now optional
people: string;
}
const schema: toZod<Res> = z.object({
films: z.string().optional(), // removing .optional() will throw an error
people: z.string(),
});
我喜欢这个解决方案,但是我最近发现它不能很好地处理联合。
这是当我发现解决方案2…
解决方案2:
这是一个备选选项,但就像@Souperman的答案一样,它不考虑null
,undefined
或可选属性(?
)。
我在这个问题线程中找到了这个解决方案。需要TS 4.9+。
使用新的TSsatisfies
关键字和z.ZodType
,我们可以检查Zod Type是否匹配TypeScript接口。如果不匹配,将抛出错误。
例如:
type User = {
id: number
name: string
age: number
}
const UserSchema = z.object({
id: z.number(),
name: z.string(),
age: z.number()
}) satisfies z.ZodType<User>
这是一个很酷的解决方案,但它不适合我,因为它不考虑null
,undefined
和可选属性。
解决方案3:
我目前正在寻找superstructjs而不是zod,因为有人在zod线程建议它。稍后再发回详细报道!