【问题标题】:Generic call signatures and type inference通用调用签名和类型推断
【发布时间】:2026-02-12 20:50:01
【问题描述】:

我有以下两个sn-ps: 1.

interface Identity {
    <T extends any>(arg: T): T
}

function identity<T>(arg: T) {
    return arg
}

let id: Identity = identity

id() // the argument here is of type unknown
interface Identity<E> {
    <T extends E>(arg: T): T
}

function identity<T>(arg: T) {
    return arg
}

let id: Identity<any> = identity

id() // the argument here is of type any

在第一种情况下,我有一个名为ObjectType 的通用调用签名,通过interface declaration 引入,在第二种情况下,我有一个generic interface declaration,在实例化时给出相同的ObjectType相同的通用调用签名。所以,AFAIK,在这两种情况下,我都应该在TypeReferences 中得到这个ObjectType

{
    <T extends any>(arg: T): T
}

但是在调用id 函数时,在第一种情况下,参数的类型是unknown,而在第二种情况下,它的类型是any。有人可以澄清两个sn-ps之间的区别吗?谢谢。

【问题讨论】:

    标签: typescript


    【解决方案1】:

    我观察到这两个接口的行为相同:

    interface Identity1 {
        <T extends any>(arg: T): T // -> unknown
    }
    
    interface Identity2 {
        <T extends E, E = any>(arg: T): T // -> any
    }
    

    您的问题的答案:两个 sn-ps 之间没有类型理论上的区别。您观察到的差异与编译器在这些不同条件下的运行方式有关,与类型论无关。

    那么为什么 TypeScript 编译器会这样做呢?

    要确定,我们必须看看 TypeScript 的实现方式。不过,我可以做出有根据的猜测:

    any 类型从 TypeScript 开始就已经存在,而 unknown 是在很久以后才引入的。因此,编译器可能仍然使用简化技巧,将T extends any 简化为简单的T,而当T 依赖于E 时,它不能应用相同的简化。

    过去,在引入unknown 之前,any 是无法推断的类型的默认类型,即不传递参数时的情况。那时这种简化非常好,但现在 unknown 是新的默认未推断类型,您会发现这种不一致。

    注意:

    interface Identity1b {
        <T extends any>(arg: T): T // -> unknown
    }
    
    interface Identity1a {
        <T>(arg: T): T // -> unknown
    }
    

    【讨论】: