首页 > 解决方案 > 在 TypeScript 中动态提取类型

问题描述

我正在尝试根据某些标志将一种类型动态映射到另一种类型,这可能会导致可选字段。

type Constraint = {
  required: boolean,
  callback: () => any,
}

type Schema = Record<string, Constraint>;

const mySchema: Schema = {
  bar: {
    required: true,
    callback: () => 1
  },
  foo: {
    required: false,
    callback: () => true
  }
}

type MapSchemaToOutput<T extends Schema> = {
  [K in keyof T as T[K]['required'] extends true ? K : never]: ReturnType<T[K]['callback']>
} & {
  [K in keyof T as T[K]['required'] extends false ? K : never]?: ReturnType<T[K]['callback']>
}

type Output = MapSchemaToOutput<typeof mySchema>;

最终目标是让 Output 相等:

{
  bar: number,
  foo?: boolean
}

我知道我可以手动进行映射,有兴趣知道这是否可以动态完成。

标签: typescripttypescript-typingsmapped-types

解决方案


您的方法按原样工作,只需进行一次更改。

问题是: Schema注释是“丢弃类型信息”:

const mySchema: Schema = {
   //...
};

有了那个注解,TS 只记住了那个mySchema对象Record<string, Constraint>任何具体结构


一种解决方法是as const

const mySchema = {
    //...
} as const;

这保留了对象中的文字类型。但是,对 的内容不再有任何限制mySchema,并且定义的任何错误mySchema都必须被用法捕获,而不是在定义时捕获。


更好的解决方法是使用辅助函数来引入约束,而不直接注释类型:

function buildSchema<T extends Schema>(schema: T) { return schema; }

const mySchema = buildSchema({
   //...
});

由于<T extends Schema>约束,如果架构对象与指定类型不匹配,TS 将像以前一样引发错误。

但与注释对象的类型不同,此函数返回的此类型与传递给函数的字面量对象相比没有变化:因此不会丢失类型信息。

通过此更改,其余类型按预期工作


推荐阅读