简体   繁体   English

TypeScript:在使用接口的对象中传递嵌套函数的 ReturnType

[英]TypeScript: pass the ReturnType of a nested function in an object that uses Interface

I want to create a structure where I have an Interface like this:我想创建一个结构,其中有一个这样的接口:

interface PageData {
    data: (this: Page) => object;
    methods: (this: Page) => { [methodName: string]: () => any };
    main: (this: Page) => void;
}

and a class that uses this data to create a new instance:以及使用此数据创建新实例的类:

class Page {
    name: string;
    data: object;
    methods: object;
    constructor(name: string, data: PageData) {
        this.name = name;
        this.data = data.data.call(this);
        this.methods = data.methods.call(this);
        data.main.call(this);
    }
}

Now how would I pass the ReturnType of the data and methods functions to Page.data and Page.methods?现在我如何将数据和方法函数的 ReturnType 传递给 Page.data 和 Page.methods? I searched for ReturnType, Generics and so on and at this point im just confused.我搜索了 ReturnType、Generics 等,此时我很困惑。 Maybe its not even possible?也许它甚至不可能? I want to have this Interface so I can use it in many files.我想要这个接口,所以我可以在很多文件中使用它。 My folder structure would be something like:我的文件夹结构类似于:

src/ src/main.ts src/Page.ts src/pages/demo.ts

In demo.ts i want to export an object with the interface PageData:在 demo.ts 中,我想导出一个带有 PageData 接口的对象:

export default {
    data: function() {
        return {
            customName: this.name + "demo"
        }
    },
    methods: function() {
        return {
            doSomething: () => this.data.customName.repeat(2)
        }
    },
    main: function() {
        console.log(this.data.customName);
        console.log(this.methods.doSomething());
    }
} as PageData

In main.ts I would get the data with require.在 main.ts 中,我会使用 require 获取数据。 Like new Page("demo", require("src/pages/demo.ts")) .new Page("demo", require("src/pages/demo.ts"))

I tried to find any example that resembles that what I am trying to do but I could not find anything.我试图找到任何与我正在尝试做的事情相似的例子,但我找不到任何东西。 The only things I found was how to get the ReturnType of a function, but not a function that is in an object that uses an Interface.我发现的唯一问题是如何获取函数的 ReturnType,而不是使用接口的对象中的函数。

With this complete example there are three errors:在这个完整的例子中,存在三个错误:
Line 27, 31: Property 'customName' does not exist on type 'object'.Property 'customName' does not exist on type 'object'.行: Property 'customName' does not exist on type 'object'.
Line 32: Property 'doSomething' does not exist on type 'object'.第 32 行: Property 'doSomething' does not exist on type 'object'.

interface PageData {
        data: (this: Page) => object;
        methods: (this: Page) => { [methodName: string]: () => any };
        main: (this: Page) => void;
}

class Page {
        name: string;
        data: object;
        methods: object;
        constructor(name: string, data: PageData) {
                this.name = name;
                this.data = data.data.call(this);
                this.methods = data.methods.call(this);
                data.main.call(this);
        }
}

export default {
        data: function() {
                return {
                        customName: this.name + "demo"
                }
        },
        methods: function() {
                return {
                        doSomething: () => this.data.customName.repeat(2)
                }
        },
        main: function() {
                console.log(this.data.customName);
                console.log(this.methods.doSomething());
        }
} as PageData

My suggestion is to make your PageData and Page types generic in the types of a Page object's data and methods properties, like this:我的建议是在Page对象的datamethods属性的类型中使您的PageDataPage类型通用,如下所示:

interface PageData<D extends object, M extends Record<keyof M, () => any>> {
  data: (this: Page<D, M>) => D;
  methods: (this: Page<D, M>) => M;
  main: (this: Page<D, M>) => void;
}

class Page<D extends object, M extends Record<keyof M, () => any>> {
  name: string;
  data: D;
  methods: M;
  constructor(name: string, data: PageData<D, M>) {
    this.name = name;
    this.data = data.data.call(this);
    this.methods = data.methods.call(this);
    data.main.call(this);
  }
}

(Note: given that your example PageData is explicitly using arrow function properties instead of methods inside its methods property, I'm not going to bother with the ThisType<T> utility type , as they don't have their own this context to worry about.) (注意:鉴于您的示例PageData显式使用箭头函数属性而不是它的methods属性中的methods ,我不会打扰ThisType<T>实用程序类型,因为它们没有自己的this上下文需要担心关于。)


Then there's the issue with making a PageData object and resolving those errors.然后是PageData对象并解决这些错误的问题。

I assume you don't want to hand-annotate the this parameter of all of its properties, and instead would like the compiler to infer the appropriate this type.我假设您不想手动注释this参数的所有属性,而是希望编译器推断出适当的this类型。 Unfortunately, that probably won't be possible all at once with a single object (see relevant GitHub comment );不幸的是,这可能无法通过单个对象一次性完成(请参阅相关的 GitHub 评论); the compiler would need to both build and infer the type at the same time, which it's not great at.编译器需要同时构建和推断类型,这不是很好。 It's better at being able to do things sequentially.能够按顺序做事更好。 So, if, as in your example, the data property isn't really dependent on anything, the methods property is dependent on data , and the main property is dependent on both data and methods , then you could consider making a builder for PageData objects that creates them sequentially:因此,如果在您的示例中, data属性并不真正依赖于任何东西, methods属性依赖于data ,而main属性依赖于datamethods ,那么您可以考虑为PageData对象制作一个构建器依次创建它们:

const pageDataBuilder = {
  data: <D extends object>(data: (this: Page<any, any>) => D) => ({
    methods: <M extends Record<keyof M, () => any>>(methods: (this: Page<D, any>) => M) => ({
      main: (main: (this: Page<D, M>) => void) => ({
        data, methods, main
      })
    })
  })
};

And here's how you could use it:以下是您如何使用它:

const data = pageDataBuilder.data(
  function () {
    return {
      customName: this.name + "demo"
    }
  }).methods(function () {
    return {
      doSomething: () => this.data.customName.repeat(2)
    }
  }).main(function () {
    console.log(this.data.customName);
    console.log(this.methods.doSomething());
  });

That compiles with no errors, and then this works:编译没有错误,然后就可以了:

const page = new Page("demo", data);
page.data.customName; // string

Looks good to me.在我看来很好。 Hope that helps;希望有所帮助; good luck!祝你好运!

Playground link to code Playground 链接到代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM