typescript能否在对象分配期间推断其他属性的类型

typescript能否在对象分配期间推断其他属性的类型,typescript,type-inference,inference,Typescript,Type Inference,Inference,我正在从事一个大型Sanity.io CMS项目,该项目涉及编写大量JS来定义以下内容类型: const person = { type: 'document', name: 'person', fieldsets: [ { name: 'personalDetails', title: 'Personal Details' }, ], fields: [ { name: 'firstName', type: 'string', fieldset: 'pers

我正在从事一个大型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' },
  ],
}
我想知道是否可以创建一个文档接口/类型,该接口/类型可以推断提供的
字段集
的名称,然后使用它们将
字段
数组中对象的
字段集
属性的类型限制为字符串文本的并集。这是一件小事,但它能让我在大量生产这些内容类型的同时利用autocomplete,从而加快开发速度

我最初的尝试是这样的:

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
  }>
}
接口文档{
类型:“文件”;
名称:字符串;
字段集:ReadonlyArray;
字段:数组
}
我理解错误并尝试了各种解决方法,但如果不使用泛型类型并提供字段集名称数组,就无法实现我的目标:

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 */
}
接口文档{
/*…定义的其余部分*/
字段集:ReadonlyArray
字段:数组
}
施工人员:文件={
/*有效,但不完全是我想要的*/
}

那么,TypeScript是否有可能在分配期间动态推断该类型,或者我正在寻找不存在的内容?

您正确地认为需要使用泛型来正确地键入该类型。Typescript无法从常量推断泛型。它可以从函数调用推断泛型。因此,这里要做的是定义一个identity函数作为创建类型安全文档的助手

我希望通过使用类型T来描述整个
fieldset
属性,而不仅仅是名称,从而获得更好的推断。但是我仍然必须使用
作为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
类型字段集={
名称:字符串;
标题:字符串;
}
类型字段={
名称:字符串;
类型:字符串;
字段集:字符串;
}
接口MyDocument{
类型:“文件”;
名称:字符串;
字段集:T;
字段:数组
}
const createDoc=(模式:MyDocument):MyDocument=>schema;
const person=createDoc({
键入:“文档”,
姓名:'人',
字段集:[
{名称:'personalDetails',标题:'personalDetails'}
]作为康斯特,
字段:[
{name:'firstName',type:'string',fieldset:'personalDetails'},
{name:'names',type:'string',fieldset:'otherSet'},//错误
],
});
//注意:我们需要'as const',因为字符串是一个对象属性
常量标识=(值:T):T=>值;
常量a=标识('personalDetails');//这就推断出一个字面意义
const b=标识({name:'personalDetails',title:'personalDetails'});//这是不可能的