使用泛型的打字稿自定义限制

时间:2019-10-23 10:45:57

标签: typescript typescript-typings

在使用库时,我发现使用泛型时对我来说似乎是一个错误:

type R<A> = A extends Bottom ? A : A
type Bottom = { test: number }

const f = <A extends Bottom>(a: A) => {
    useIt(a) // type error here
}

const useIt = <A extends Bottom>(a: R<A>) => console.log(a)

您也可以在 Playground example,由于某些不清楚的原因,a不能用作R<A>,即使这种类型等效于A

类型错误是:

Argument of type 'A' is not assignable to parameter of type 'R<A>'.
  Type 'Bottom' is not assignable to type 'R<A>'.

使用具体类型代替泛型将按预期工作,例如:

type X = {test: 1}
const x: R<X> = {test: 1} // all good
const noX: R<X> = {test: 2} // error

对于特定类型,具有更好的限制类型也可以正常工作:

type R<A> = A extends Bottom ? A : never
const x: R<X> = {test: 1} // all good
const error: R<{}> = {} // type error as expected given that {} doesn't extend Bottom

那么,有什么方法可以使其与泛型一起使用?

2 个答案:

答案 0 :(得分:1)

更多的是设计限制,而不是错误;未解决的条件类型(取决于尚未指定的泛型类型参数)或多或少地由编译器完全推迟,并且几乎没有东西可分配给它们。


有一个未解决的问题,microsoft/TypeScript#23132建议使用泛型约束来确定对未解决的条件类型的可分配性;我认为,如果实施了此建议,您的示例代码将起作用(因为A extends Bottom被认为是正确的)...因此,您可能想要解决该问题并给它加上?,并在可能的情况下解释您的用例比那里的东西更具吸引力。

还有microsoft/TypeScript#33912,它建议使用控制流分析来确定对未解决的条件类型的可分配性,如果要实现它,这也可能会有所帮助。


现在,我认为“使其正常工作”的唯一方法是使用type assertions,如下所示:

useIt(a as R<A>)

或表示您的类型,以便尽可能不再是无法解析的条件类型;在您的示例代码中,R<A>是无条件A,因此

// type R<A> = A extends Bottom ? A : A
type R<A> = A

将解决它。

实际上,我看到您在代码的另一部分将R<A>更改为本质上为Extract<A, Bottom>。在某些情况下,Extract<T, U>可以用交集T & U代替,而不会产生不良影响;您可以尝试以下方法:

// type R<A> = A extends Bottom ? A : never
type R<A> = A & Bottom

这也可能有效。


好的,希望能有所帮助;祝你好运!

Link to code

答案 1 :(得分:0)

经过大量修改,我通过显式添加限制来解决此问题:

const f = <A extends Bottom>(a: R<A>) => {
    useIt(a) // works
}

const useIt = <A extends Bottom>(a: R<A>) => console.log(a)

请注意,现在f参数具有与useIt相同的约束,这将使编译器满意。事后看来,这实际上是有道理的,因此我们100%确信该类型也可用于useIt:)