【问题标题】:Recursive type alias property access递归类型别名属性访问
【发布时间】:2020-12-01 17:58:49
【问题描述】:

我正在尝试键入这个最初用 JavaScript 编写的类。

这里是代码sn-p。

type SchemaDefinition<Schema> = {[Key in keyof Schema]: Schema[Key][] | {[K in keyof Schema[Key]]: SchemaDefinition<Schema[Key][K]>}};
type Middlewares<Schema> = {[Key in keyof Schema]: (value: Schema[Key]) => Schema[Key] | {[K in keyof Schema[Key]]: Middlewares<Schema[Key][K]>}};

class ObjectGenerator<Schema> {
  private schemaDefinition: SchemaDefinition<Schema>;

  constructor(schemaDefinition: SchemaDefinition<Schema> = {} as SchemaDefinition<Schema>) {
    this.schemaDefinition = schemaDefinition;
  }

  generate(middlewares: Middlewares<Schema> = {} as Middlewares<Schema>): Schema {
    const {schemaDefinition} = this;

    return Object.entries(schemaDefinition).reduce((schema, [property, values]) => {
      if (Array.isArray(values)) {
        return {...schema, [property]: (middlewares[property] || ((x: any): any => x))(values[Math.floor(Math.random() * values.length)])};
      } else if (typeof values === "object") {
        return {...schema, [property]: new ObjectGenerator(values).generate(middlewares[property])};
      }

      return schema;
    }, {} as Schema);
  }
}

我得到的错误是。

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Middlewares<Schema>'. No index signature with a parameter of type 'string' was found on type 'Middlewares<Schema>'

我对此的期望是 TypeScript 不仅可以猜测我在编译时传递给它的东西的类型(比如 Schema 变成了 SchemaDefinition),而且还支持递归调用,并且仍然能够猜测返回输出中嵌套属性的类型,以便客户端在尝试访问不存在的嵌套属性、在不存在的属性上调用中间件或使用类型错误。

【问题讨论】:

  • Object.entries() 是问题所在,因为编译器不能确定它知道所有对象的属性;请参阅this answer 了解更多信息。如果你想回避它,你可以做一个像Object.entries(schemaDefinition) as Array&lt;[keyof Schema, Schema[keyof Schema]]&gt; 这样的断言(不比{} as SchemaDefinition&lt;Schema&gt; 差,这对我来说似乎更可疑)。如果这是您的主要问题,我可能会将问题作为引用 Object.entries() 的其他人的副本来关闭;否则请详细说明。祝你好运!
  • 谢谢你。实际上,使用 for-in 循环有助于使事情变得更清晰。我忘记了回调中间件中联合周围的一些括号。通过添加一些丑陋的类型断言,我终于得到了一些有用的东西。谢谢。我会回答我自己的问题。

标签: typescript recursion


【解决方案1】:

Middlewares 属性中的回调有问题,它错过了一些周围的括号。

此外,代码看起来更简洁,使用for-in 循环更易于使用。我也借此机会把它变成了一个函数,因为在这种情况下使用类并没有真正的附加值。

我不得不使用一些丑陋的as 类型断言,但据我了解,我不明白为什么在这种情况下不这样做,因为它是一个联合类型,它可以帮助编译器知道是什么。

type SchemaDefinition<Schema> = {[Key in keyof Schema]: Schema[Key][] | {[K in keyof Schema[Key]]: Schema[Key][K][]}};
type Middlewares<Schema> = {[Key in keyof Schema]?: ((value: Schema[Key]) => Schema[Key]) | Middlewares<Schema[Key]>};

const createObjectGenerator = <Schema>(schemaDefinition = {} as SchemaDefinition<Schema>) => {
  return (middlewares = {} as Middlewares<Schema>) => {
    const schema = {} as Schema;

    for (const property in schemaDefinition) {
      if (!schemaDefinition.hasOwnProperty(property)) {
        continue;
      }

      const values = schemaDefinition[property];

      if (Array.isArray(values)) {
        const value = values[Math.floor(Math.random() * values.length)];

        if (typeof middlewares !== "undefined") {
          const middleware = middlewares[property];

          if (typeof middleware === "function") {
            schema[property] = middleware(value);
            continue;
          }
        }

        schema[property] = value;
        continue;
      }

      if (typeof values === "object") {
        if (typeof middlewares !== "undefined") {
          const nestedMiddlewares = middlewares[property];

          if (typeof nestedMiddlewares === "object") {
            schema[property] = createObjectGenerator(values as Schema[typeof property])(nestedMiddlewares) as Schema[typeof property];
            continue;
          }  
        }

        schema[property] = createObjectGenerator(values as Schema[typeof property])() as Schema[typeof property];
        continue;
      }
    }

    return schema;
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-24
    • 2012-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-11
    • 1970-01-01
    相关资源
    最近更新 更多