简体   繁体   English

A什么时候不在TypeScript中扩展A

[英]When does A not extends A in TypeScript

For reasons not relevant to the question (but which include fun and profit in type-level programming), one of my types eventually boils down to the following minimal example:由于与问题无关的原因(但包括类型级编程中的乐趣和利润),我的一种类型最终归结为以下最小示例:

type IsTrue<A extends true> = A
type Refl<M> = M extends M ? true : false
type Proof<M> = IsTrue<Refl<M>>

... which results in a compile error. ...这会导致编译错误。 Now, we can discuss how I ended up here, and it is related to a (probably) wrong way of encoding Type Equality.现在,我们可以讨论一下我是如何走到这一步的,与(可能)错误的 Type Equality 编码方式有关。 But the question remains: When doesn't M extends M resolves to true for all M ?但是问题仍然存在: M extends M什么时候不对所有M解析为true What's the counter-example?反例是什么? How would one go to fix this (possibly by constraining M )?一个 go 将如何解决这个问题(可能通过约束M )?

Conditional types , as originally implemented in microsoft/TypeScript#21316 are not always evaluated eagerly .最初在microsoft/TypeScript#21316中实现的条件类型并不总是急切地评估。 If they depend on an as-yet-unspecified generic type parameter, like the M inside the body of the Refl<M> definition, the compiler will generally defer evaluation of the conditional type.如果它们依赖于尚未指定的泛型类型参数,例如Refl<M> M主体内的 M,则编译器通常会推迟对条件类型的评估。 Note that the details about exactly when and where the compiler will evaluate a conditional type are not spelled out in any documentation, and they've been evolving over time with new releases of TypeScript, so I can't say anything with absolute certainty here.请注意,关于编译器评估条件类型的确切时间和地点的详细信息没有在任何文档中详细说明,并且随着时间的推移,随着 TypeScript 的新版本的发布,它们一直在发展,所以我在这里不能绝对肯定地说任何话。 But the general situation is as I've described it.但总体情况正如我所描述的那样。

You were expecting the compiler to look at M extends M? true: false您期望编译器查看M extends M? true: false M extends M? true: false and eagerly reduce it to true so that your definition would be equivalent to type Refl<M> = true , either inside the definition of Refl<M> or inside the definition of Proof<M> . M extends M? true: false并急切地将其简化为true以便您的定义等同于type Refl<M> = true ,无论是在Refl<M>的定义内还是在Proof<M>的定义内。 Neither of these evaluations happen;这些评估都没有发生; they are deferred because in both cases M is an unspecified type parameter.它们被推迟是因为在这两种情况下M都是未指定的类型参数。 So the compiler cannot be sure that Refl<M> will be any narrower than the union true | false所以编译器不能确定Refl<M>会比联合true | false更窄。 true | false (also known as the boolean type ), and thus is not known to satisfy the constraint for IsTrue<A extends true> . true | false (也称为boolean类型),因此不知道满足IsTrue<A extends true>约束

So it isn't that M extends M? true: false所以不是M extends M? true: false M extends M? true: false should ever actually evaluate to false (as far as I know it can't), but that the compiler fails to evaluate it at all in order to simplify it to true . M extends M? true: false实际上应该评估为false (据我所知它不能),但是编译器根本无法评估它以将其简化为true

I don't see any GitHub issues about this specific circumstance, but there are many such issues which boil down to the compiler's inability to analyze conditional types that depend on an unresolved generic type parameter.我没有看到任何关于此特定情况的 GitHub 问题,但有许多此类问题归结为编译器无法分析依赖于未解决的泛型类型参数的条件类型。 For a relatively recent example, see microsoft/TypeScript#46795 .有关相对较新的示例,请参阅microsoft/TypeScript#46795


Note that the particular form M extends... ? ... : ...请注意,特定形式M extends... ? ... : ... M extends... ? ... : ... where M is a plain generic type parameter is known as a distributive conditional type and so any unions in M would be broken into individual members before being evaluated. M extends... ? ... : ...其中M是一个普通的泛型类型参数被称为分布式条件类型,因此M中的任何联合在被评估之前都会被分解为单独的成员。 This doesn't affect whether Refl<M> could ever be wider than true , but it can affect the output type:这不会影响Refl<M>是否可以比true更宽,但它会影响 output 类型:

type Refl<M> = M extends M ? true : false
type Hmm = Refl<never> // never
type Refl2<M> = [M] extends [M] ? true : false;
type Hmm2 = Refl2<never> // true

Refl<M> is distributive over unions in M , and never is considered to be "the empty union" (see this comment in ms/TS#23182 ) and thus the output is also the empty union. Refl<M>M中的联合上分配,并且never被认为是“空联合”(参见ms/TS#23182 中的此评论),因此 output 也是空联合。 But Refl2<M> is not distributive (since [M] is not a plain generic type parameter) and so Refl2<never> is true .但是Refl2<M>不是分布式的(因为[M]不是普通的泛型类型参数),所以Refl2<never>true Both never and true are assignable to true , though, so IsTrue<Refl<M>> would work out no matter what.但是, nevertrue都可以分配给true ,因此IsTrue<Refl<M>>无论如何都可以解决。 But it's trickier than it might seem to demonstrate that.但这比它看起来要证明的要棘手。


It is conceivable that a feature could be introduced whereby conditional types of the form X extends X? Y: Z可以想象,可以引入一个特征,即 X 形式的条件类型X extends X? Y: Z X extends X? Y: Z could be eagerly reduced to Y in cases where Y does not depend on X (your case) or where X is not a plain generic type parameter (not your case). X extends X? Y: ZY不依赖于X (您的情况)或X不是普通泛型类型参数(不是您的情况)的情况下,Z 可以急切地简化为Y But such a feature would have a negative effect on compiler performance, since it would need to check every conditional type for this situation even though the vast majority of conditional types are not like this;但是这样的特性会对编译器性能产生负面影响,因为即使绝大多数条件类型不是这样,它也需要检查这种情况下的每个条件类型; features need to pay for themselves, and this one probably wouldn't.功能需要自己付费,而这个可能不会。 Even worse, there's probably a lot of real-world code that either intentionally or unintentionally depends on the compiler deferring conditional types like this, and such a feature would be a large breaking change.更糟糕的是,可能有很多现实世界的代码有意或无意地依赖于编译器延迟这样的条件类型,这样的特性将是一个巨大的突破性变化。


Finally, if you're just interested in a workaround, my usual approach is as follows: If you're sure that T extends U but the compiler isn't, then you can't use T in a place that expects something assignable to U .最后,如果您只是对解决方法感兴趣,我通常的方法如下:如果您确定T extends U但编译器没有,那么您不能在期望可以分配的地方使用T U But you can use Extract<T, U> .但是您可以使用Extract<T, U> The Extract<T, U> utility type is primarily meant to filter any unions in T so that only those members assignable to U are left. Extract<T, U>实用程序类型主要用于过滤T中的任何联合,以便只留下那些可分配给U的成员。 But the compiler does see Extract<T, U> is assignable to both T and U (possibly in ms/TS#29437 ), and if you're right that T extends U , then Extract<T, U> will eventually evaluate to just T as desired.但是编译器确实看到Extract<T, U>可以同时分配给TU (可能在ms/TS#29437中),如果T extends U是正确的,那么Extract<T, U>最终将评估为只是T根据需要。 So this off-label use of Extract does what we want:因此, Extract的这种标签外使用可以满足我们的要求:

type Proof<M> = IsTrue<Extract<Refl<M>, true>> // okay

This is almost like a type-level type assertion , so similar caveats apply.这几乎就像一个类型级别的类型断言,所以类似的警告也适用。 If you're wrong about Refl<M> being assignable to true , then IsTrue<Extract<Refl<M>, true>> would still compile, but you're no longer evaluating IsTrue<Refl<M>> , but something more like IsTrue<never> or IsTrue<true> .如果您对Refl<M>可分配给true有误,那么IsTrue<Extract<Refl<M>, true>>仍然可以编译,但您不再评估IsTrue<Refl<M>> ,而是更多像IsTrue<never>IsTrue<true> So be careful!所以要小心!

Playground link to code Playground 代码链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 TypeScript中的扩展如何工作? - How does extends in TypeScript work? “扩展”在打字稿中实际上意味着什么? - What does 'extends' actually mean in typescript? 在 nodejs 打字稿中扩展类时未定义的函数 - Undefined function when extends class in nodejs typescript 为什么在 typescript 中扩展 Class 时没有类型提示 - why there is no type hint when extends Class in typescript TypeScript中的&#39;extends&#39;与C#中泛型的约束作用不同 - 'extends' from TypeScript does not the same job as constraint on generics in C# 打字稿类型“对象不满足类型参数的约束&#39;扩展TE&#39; - Typescript Type "Object does not satisfy the constraint 'extends TE' for type parameter 在 Typescript 中记录了什么<k extends keyof any, t>意思是?</k> - In Typescript what does Record<K extends keyof any, T> mean? 为什么 typescript 不能正确推断 T[K]<t extends person, k keyof t> 通用的?</t> - Why typescript does not properly infer T[K] with <T extends Person, K extends keyof T> generic? typescript 扩展盲输入时如何定义响应 - typescript how to define the response when it extends a blind input 在打字稿中使用 react-final-form FieldRenderProps 扩展接口时发生冲突 - conflict when extends interface with react-final-form FieldRenderProps in typescript
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM