繁体   English   中英

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

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

我想创建一个结构,其中有一个这样的接口:

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);
    }
}

现在我如何将数据和方法函数的 ReturnType 传递给 Page.data 和 Page.methods? 我搜索了 ReturnType、Generics 等,此时我很困惑。 也许它甚至不可能? 我想要这个接口,所以我可以在很多文件中使用它。 我的文件夹结构类似于:

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

在 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

在 main.ts 中,我会使用 require 获取数据。 new Page("demo", require("src/pages/demo.ts"))

我试图找到任何与我正在尝试做的事情相似的例子,但我找不到任何东西。 我发现的唯一问题是如何获取函数的 ReturnType,而不是使用接口的对象中的函数。

在这个完整的例子中,存在三个错误:
Property 'customName' does not exist on type 'object'.行: Property 'customName' 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

我的建议是在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);
  }
}

(注意:鉴于您的示例PageData显式使用箭头函数属性而不是它的methods属性中的methods ,我不会打扰ThisType<T>实用程序类型,因为它们没有自己的this上下文需要担心关于。)


然后是PageData对象并解决这些错误的问题。

我假设您不想手动注释this参数的所有属性,而是希望编译器推断出适当的this类型。 不幸的是,这可能无法通过单个对象一次性完成(请参阅相关的 GitHub 评论); 编译器需要同时构建和推断类型,这不是很好。 能够按顺序做事更好。 因此,如果在您的示例中, 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
      })
    })
  })
};

以下是您如何使用它:

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());
  });

编译没有错误,然后就可以了:

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

在我看来很好。 希望有所帮助; 祝你好运!

Playground 链接到代码

暂无
暂无

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

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