typescript - 打字稿可以在对象分配期间推断其他属性的类型
问题描述
我正在开发一个大型 Sanity.io CMS 项目,该项目涉及编写大量 JS 来定义内容类型,如下所示:
const person = {
type: 'document',
name: 'person',
fieldsets: [
{ name: 'personalDetails', title: 'Personal Details' },
],
fields: [
{ name: 'firstName', type: 'string', fieldset: 'personalDetails' },
{ name: 'surname', type: 'string', fieldset: 'personalDetails' },
],
}
我想知道是否可以创建一个 Document 接口/类型来推断所fieldsets
提供的名称,然后使用它们将数组fieldset
中对象的属性类型限制fields
为字符串文字的联合。这是一件小事,但它可以让我在大量制作这些内容类型的同时利用自动完成功能,从而加快开发速度。
我最初的尝试是这样的:
interface Document {
type: 'document';
name: string;
fieldsets: ReadonlyArray<{ name: string; title: string; }>;
fields: Array<{
name: string;
type: string;
fieldset: this['fieldset'][number]['name']; // Error: A 'this' type is available only in a non-static member of a class or interface
}>
}
我了解该错误并尝试了各种解决方法,但如果不使用泛型类型并提供一组字段集名称,则无法实现我的目标:
interface Document<T extends ReadonlyArray<string>> {
/* ...rest of definition */
fieldset: ReadonlyArray<{ name: T[number] }>
fields: Array<{
name: string;
type: string;
fieldset: T[number];
}>
}
const person: Document<['personalDetails']> = {
/* works, but not exactly what I'm after */
}
那么 TypeScript 是否有可能在分配期间动态推断该类型,还是我在寻找不存在的东西?
解决方案
您是正确的,您需要使用泛型来正确键入它。Typescript 无法从常量中推断出泛型。它可以从函数调用中推断出泛型。因此,您在这里要做的是定义一个身份函数作为创建类型安全文档的助手。
我希望通过使用类型 T 来描述整个fieldsets
属性而不仅仅是名称来获得更好的推理。但是我仍然必须使用as const
才能从对象属性中获取文字字符串值。
type FieldSet = {
name: string;
title: string;
}
type Field = {
name: string;
type: string;
fieldset: string;
}
interface MyDocument<T extends ReadonlyArray<FieldSet>> {
type: 'document';
name: string;
fieldsets: T;
fields: Array<Field & {fieldset: T[number]['name']}>
}
const createDoc = <T extends ReadonlyArray<FieldSet>>(schema: MyDocument<T>): MyDocument<T> => schema;
const person = createDoc({
type: 'document',
name: 'person',
fieldsets: [
{ name: 'personalDetails', title: 'Personal Details' }
] as const,
fields: [
{ name: 'firstName', type: 'string', fieldset: 'personalDetails' },
{ name: 'surname', type: 'string', fieldset: 'otherSet' }, // error
],
});
// note: we need `as const` because the string is an object property
const identity = <T extends any>(value: T): T => value;
const a = identity('personalDetails'); // this infers a literal
const b = identity({ name: 'personalDetails', title: 'Personal Details' }); // this does not
推荐阅读
- javascript - 从循环渲染本机反应
- c# - Newtonsoft.Json:当有附加属性时如何将 json 转换为 xml?
- java - java中可以为null的返回值是否可以放入kotlin中声明为非null的变量中
- python - Python 错误,与未散列的可变 Series 对象有关
- javascript - useEffect 中的依赖数组 - 我应该传递所有参数
- python - tensorflow with GPU,怎么看tensorflow是用GPU的?
- php - 如何在 WooCommerce 管理订单列表中的买家姓名后添加第一个订单消息
- azure - 使用参数化数据库连接部署 Azure 数据工厂
- angular - 无法在 Angular 中的组件之间发送数据
- python - 显示所选文件夹中的图像