打字稿`可以用不同的约束子类型实例化`错误

Posted

技术标签:

【中文标题】打字稿`可以用不同的约束子类型实例化`错误【英文标题】:Typescript `could be instantiated with a different subtype of constraint` error 【发布时间】:2021-08-14 18:26:04 【问题描述】:

当我返回的结果应该符合函数的预期类型时,我不断收到could be instantiated with a different subtype of constraint 错误,我无法理解我做错了什么。这是我的代码,删除了所有不相关的细节:

type User =  

interface BasePageProps 
    user: User


type GetServerSidePropsResult<P> =
  |  props: P 

type PropsWithUser = <TProps extends BasePageProps>(
    callback: (user: User) => Promise<Omit<TProps, 'user'>>
) => Promise<GetServerSidePropsResult<TProps>>

export const testFunc: PropsWithUser = async (callback) => 
    const user = 
    return  props: 
            user,
            ...(await callback(user))
         

这是我得到的错误:

' 用户:; & Omit' 可分配给类型为“TProps”的约束,但可以使用约束“BasePageProps”的不同子类型来实例化“TProps”。

Here's this example on TS Playground.

为什么用不同子类型实例化的潜在TProps 是个问题?我该如何解决?

P.S.:您可能会注意到这些类型取自 Next.js。不过这个问题跟 Next.js 本身没有关系,所以请不要给这个问题打标签。

【问题讨论】:

【参考方案1】:

你的函数的有效签名是:

type PropsWithUser = <TProps extends BasePageProps>(
    callback: (user: User) => Promise<Omit<TProps, 'user'>>
) => Promise<GetServerSidePropsResult<BasePageProps & Omit<TProps, 'user'>>>

或者你可以删除一些重复:

type PropsWithUser = <TProps extends BasePageProps, TO = Omit<TProps, 'user'>(
    callback: (user: User) => Promise<TO>
) => Promise<GetServerSidePropsResult<BasePageProps & TO>>

证明编译器正确引发代码错误的反例。让我们定义一个扩展 BasePageProps 但不通过函数结果类型检查的 TProps 类型的具体示例:

type User =  

type AnotherUser =  a: string 

interface TProps 
    user: AnotherUser


interface BasePageProps 
    user: User


type Eq = TProps extends BasePageProps ? true : false // type Eq = true

type PropsWithUser = <TProps extends BasePageProps>(
    callback: (user: User) => Promise<Omit<TProps, 'user'>>
) => Promise<GetServerSidePropsResult<TProps>>

TS playground

所以你的函数应该返回类型为Promise&lt; props: user: AnotherUser, ... &gt; 的结果。它显然没有。

正如错误所说:' user: ; &amp; Omit&lt;TProps, "user"&gt;' is assignable to the constraint of type 'TProps', but 'TProps' could be instantiated with a different subtype of constraint 'BasePageProps'

type User =  

interface BasePageProps 
    user: User


type AnotherUser =  a: string 

interface ConcreteTProps 
    user: AnotherUser


//  user: ;  & Omit<TProps, "user"> is assignable
// to the constraint of type TProps (i.e. BasePageProps)
const a: BasePageProps =  user:  

// but 'TProps' could be instantiated with a different 
// subtype of constraint 'BasePageProps' (i.e. ConcreteTProps)
// Which is a subtype of BasePageProps by cannot be 
// assigned with  user: User 
const b: ConcreteTProps =  user:   // error

【讨论】:

没想到字段的类型也可以在扩展接口上扩展,谢谢!

以上是关于打字稿`可以用不同的约束子类型实例化`错误的主要内容,如果未能解决你的问题,请参考以下文章

可以使用不同的约束子类型来实例化函数

特定 javascript 实例化模式的正确打字稿定义是啥

从打字稿的目录中实例化所有类

我如何在函数体中使用通用类型

打字稿:如何将对象映射到类型?

打字稿中具有泛型的子类型约束