具有泛型语法的 TypeScript 可重用函数签名

问题描述 投票:0回答:1

嗨,我想知道可重用函数类型(相同的参数、返回类型、可由多个函数重用)和泛型的最佳语法是什么?

当时我想不出更短的语法:

type CountResult<T extends object> = (arg: T) => [T, number]

function log<T extends object>(arg: T){
    console.log(arg)
}

function countWithKeys<T extends object>(arg: Parameters<CountResult<T>>[0]): ReturnType<CountResult<T>> {
    const count = Object.keys(arg).length
    log<T>(arg) // Need to be able to access T
    return [arg, count]

}

function countWithLoop<T extends object>(arg: Parameters<CountResult<T>>[0]): ReturnType<CountResult<T>>{
    let count = 0;
    for(const i in arg) {
        count++;
    }
    log<T>(arg)
    return [arg, count]
}

我基本上是在问重用泛型函数的类型签名的最佳语法是什么。

typescript function generics syntax
1个回答
0
投票

您的generic类型参数的范围不正确,或者至少不利于用于您的目的。泛型函数调用签名上具有类型参数(如

type F = <T>(t: T)=>void
),而泛型类型的参数在类型名称上(如
type F<T> = (t: T)=>void
)。这些是不同的;请参阅泛型参数放置之间的打字稿差异以获取更多讨论。

您真正想要的类型是这样的:

type CountResult = <T extends object>(arg: T) => [T, number]

要为

countWithKeys
countWithLoop
提供该类型,您需要从使用
function
语句
更改为函数 表达式(例如
function
表达式
箭头功能)。

这是因为我们目前无法在 TypeScript 中直接注释函数语句。 microsoft/TypeScript#22063 有一个支持函数语句注释的开放功能请求,但不幸的是,目前它们还不是该语言的一部分。

所以让我们使用箭头函数:

const countWithKeys: CountResult = arg => {    
    const count = Object.keys(arg).length
    log(arg);
    return [arg, count]
}

const countWithLoop: CountResult = arg => {    
    let count = 0;
    for (const i in arg) {
        count++;
    }
    log(arg);
    return [arg, count]
}

编译没有错误,并且

arg
类型参数是 contextually 给定适当的泛型类型。看起来不错,我们都完成了。


好吧,除了您显然需要在函数的实现中引用泛型类型参数

T
。您的示例是您希望能够调用
log<T>(arg)
而不是
log(arg)
。这绝对没有必要,因为编译器会很乐意为您推断类型参数。但为了继续下去,让我们假装
log(arg)
不起作用,而你确实需要
log<T>(arg)
。想必在真实的代码中有一个更有说服力的用例。

无论如何,在上面的函数实现中,你想要的类型参数是anonymous,你不能直接使用它。为了解决这个问题,您需要以某种方式给该类型一个名称。最直接但冗长的方法是显式且完整地注释实现,包括相关的类型参数声明:

const countWithKeys: CountResult = <T extends object>(arg: T) => {    
    const count = Object.keys(arg).length
    log<T>(arg);
    return [arg, count]   
}

另一种方法是尝试以其他方式获取对匿名类型的引用。对于

CountResult
arg
参数属于相关类型,因此您可以使用
typeof
类型运算符
:

const countWithLoop: CountResult = arg => {    
    type T = typeof arg;
    let count = 0;
    for (const i in arg) {
        count++;
    }
    log<T>(arg);
    return [arg, count]
}

这有点不那么冗长,但更难概括并且可能更令人困惑。

这里没有“完美”的方法;您要么需要让编译器为您推断事物,从而使它们匿名,要么需要显式指定和注释这些事物,从而使它们可能是多余的。这已经是最好的了。

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.