【问题标题】:How to define a type for a function like this?如何为这样的函数定义类型?
【发布时间】:2019-10-09 22:19:07
【问题描述】:

我正在将一些代码移至 TS,并且一直在努力为一组路由函数定义一个类型。这就是他们的样子:

const root: Route = () => 'root'
root.child = () => `${root()}/child`
root.child.grandchild = () => `${root.child()}/grandchild`

我尝试在嵌套属性的索引签名旁边定义某种递归可调用类型或接口,但没有多大成功:

type Route = {
  (): string
  [key: string]: Route
}

关于我如何做到这一点的任何想法或想法?

游乐场:http://www.typescriptlang.org/play/index.html?ssl=1&ssc=1&pln=10&pc=1#code/C4TwDgpgBASg9gV2NAvFA3gKCjqAKASgC4oBnYAJwEsA7Ac21wG0BrCEE86+gXRPiQRMAX0yYAxnBrkoFOHGD9EyKGkKqAfFADkchdsx7gAOnEALKgBsAJqvwFNUAAYASdEcLCA9OavWnhvImvjbGdBQAhjTWIbZqDiharu5BphY2nl7hUTHp-mJAA

【问题讨论】:

    标签: typescript


    【解决方案1】:

    根据您要执行的操作,有多种选择。

    如果您使用常规的function 定义,您可以在声明函数的范围内向函数添加额外的属性,TS 会将这些属性识别为函数上的属性:

    function root() { return 'root' }
    function child() { return `${root()}/child` }
    child.grandchild = () => `${root.child()}/grandchild`
    root.child = child;
    
    
    
    root.child.grandchild() //ok
    

    Play

    另一种选择是使用Object.assign 一次性创建具有属性的函数:

    
    const root = Object.assign(() => 'root', {
        child: Object.assign(() => `${root()}/child`, {
            grandchild: () => `${root.child()}/grandchild`
        })
    });
    
    root.child.grandchild()
    

    Play

    这些选项都没有实际使用您的Router 接口,该接口允许以后添加任何字符串属性。为此,我认为最简单的选择是创建一个辅助函数,该函数将在内部使用Object.assign,但也将使用所需的类型:

    type Route = {
        (): string
        [key: string]: Route
    }
    
    function createRoute(fn: () => string, other: Record<string, Route> = {}) {
        return Object.assign(fn, other);
    }
    
    const root: Route = createRoute(() => 'root', {
        child: createRoute(() => `${root()}/child`, {
            grandchild: createRoute(() => `${root.child()}/grandchild`)
        })
    })
    

    Play

    【讨论】:

    • 其实那些箭头函数已经写好了,还有一大堆,我宁愿保留它,只是想办法为它们定义一个类型。我会玩弄你的建议,看看它的样子。谢谢!
    • 啊,this 是阻止更直接答案的原因之一吗?我猜由于某种原因,可调用类型永远不会获得隐式索引签名。
    • @GuilhermeBaron 总是有type assertions 所以const root = (() =&gt; 'root') as Route;root.child = (() =&gt; `${root()}/child`) as Route; 等等。奇怪的是这将是必要的...
    • @jcalz 是的,断言总是一种选择。我认为在这种情况下可能是最简单的。不要认为在这种情况下会丢失任何类型安全性。
    • This 可能是相关的。我希望能够做类似that 的事情,但看起来 TS 允许我只向函数添加一层深度属性。使用类型断言我没有得到类型安全,例如一些子路由箭头函数在调用这些函数时需要参数并使用未经 TS 验证的断言。
    猜你喜欢
    • 2022-06-15
    • 2021-03-18
    • 2010-12-17
    • 2021-07-24
    • 1970-01-01
    • 2011-09-25
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多