简体   繁体   English

为什么 `T extends B` 不能在将来的引用中将 T 限制为 B?

[英]Why doesn't `T extends B` work to limit T to B in future references?

In this TypeScript code , also repeated below, the type PickedAttributeSimpler is a generic type with a parameter.这个 TypeScript 代码中,同样在下面重复,类型PickedAttributeSimpler是带有参数的泛型类型 The parameter type is restricted (using extends ) to a pretty small set - in this example, just one specified possibility, but the restriction doesn't have to always be so narrow.参数类型被限制(使用extends )到一个非常小的集合 - 在这个例子中,只有一个指定的可能性,但限制不必总是那么窄。

Later in the type definition, I would expect that references to the generic type parameter would be narrowed to the type the parameter was restricted to, ie what's on the right side of the extends keyword.稍后在类型定义中,我希望对泛型类型参数的引用会缩小到参数所限制的类型,即extends关键字右侧的内容。 In any particular use context, it might even be narrower.在任何特定的使用环境中,它甚至可能更窄。 However, in this example and in several more complicated ones eventually boiled down to this one, replacing references to the type parameter with the type that parameter is limited to (to the right of extends ) produced the desired behavior, allowing TypeScript to figure out the type of the generic as a whole.然而,在这个例子和几个更复杂的例子中,最终归结为这个例子,用参数限制的类型(在extends右侧)替换对类型参数的引用产生了所需的行为,允许 TypeScript 找出泛型作为一个整体的类型。

Unfortunately, in practice, some additional narrowing is sometimes needed, where an instance of the type specifies a parameter more specific than the type that parameter must be limited to, and additional references to the generic type parameter are needed to track that through consistently - so just dropping the use of generic type parameters altogether is not a valid solution beyond this simplified example.不幸的是,在实践中,有时需要进行一些额外的缩小,其中类型的实例指定的参数比该参数必须限制的类型更具体,并且需要对泛型类型参数的额外引用以始终如一地跟踪它 - 所以除了这个简化的例子之外,仅仅完全放弃使用泛型类型参数并不是一个有效的解决方案。

Instead, a better understanding is sought of the conditions in which limiting the parameter type will NOT result in later references to the generic type parameter reflecting that narrowing.相反,寻求更好地理解限制参数类型不会导致以后引用反映该缩小的泛型类型参数的条件。

Separate questions address why some simplifications that appear possible from this example such as hard-coding the ObjectModelMap or using a conditional type in ObjectModel don't adequately preserve the issue being demonstrated.单独的问题解决了为什么此示例中可能出现的一些简化(例如硬编码 ObjectModelMap在 ObjectModel 中使用条件类型)不能充分保留所演示的问题。

It is possible this is due to a bug in TypeScript.这可能是由于 TypeScript 中的错误造成的。 The closest issue I could find is #33014 but that seems to be more specific about functions than defining other types.我能找到的最接近的问题是#33014 ,但这似乎比定义其他类型更具体地描述了函数。 Maybe I just don't understand what's happening here well enough to search with the right terms or report it.也许我只是不太了解这里发生的事情,无法使用正确的术语进行搜索或报告。 (Please feel free to file a new bug report there if you think it's appropriate and not duplicative). (如果您认为合适且不重复,请随时在此处提交新的错误报告)。


The code at the Playground link is: Playground 链接上的代码是:

import BN from "bn.js";

//Simplified version of externally fixed model; many fields omitted for simpler example
type FieldNameWithValueMap = {
    Moon: [fieldName: "visibleStarsCount", fieldValue: BN] | [fieldName: "hostStar", fieldValue: string];
    Planet: [fieldName: "visibleStarsCount", fieldValue: BN] | [fieldName: "hostPlanet", fieldValue: string];
}
//type PlanetaryBodyName = keyof FieldNameWithValueMap; //'Moon' | 'Planet'; not used for simpler example
type TupleToObject<T extends [string, any]> = { [key in T[0]]: Extract<T, [key, any]>[1] };
type ObjectModel<T extends keyof FieldNameWithValueMap> = TupleToObject<FieldNameWithValueMap[T]>;
//Hovering over these two examples, the types are as expected; they need to be derived from imported types.
type MoonObject2 = ObjectModel<'Moon'>; //{visibleStarsCount: BN; hostPlanet: string;}
type PlanetObject2 = ObjectModel<'Planet'>; //{visibleStarsCount: BN; hostStar: string;}
type DRYObjectModelMap = {
    [PlanetaryBodyClass in keyof FieldNameWithValueMap]: ObjectModel<PlanetaryBodyClass>
}
type PickedAttributeSimpler<T extends 'Moon'> = DRYObjectModelMap[T]['visibleStarsCount'];
//The error disappears when the definition below rather than above is used.
//type PickedAttributeSimpler<T extends 'Moon'> = DRYObjectModelMap['Moon']['visibleStarsCount'];
function genericFunction<
    T extends 'Moon', //union type that includes | 'Planet' omitted for simpler example.
    //Omitting the generic type parameter and hard-coding it into each usage below fixes the error,
    //but the real version of the function not oversimplified for the example needs to be generic.
>(
    PlanetaryBodyClass: T,
) : PickedAttributeSimpler<T> { //Same if using DRYObjectModelMap[T]['visibleStarsCount']
    return new BN(42); //ERROR TS(2322): Type 'BN' is not assignable to type 'PickedAttributeSimpler<T>'.
}
type ExamplePickedAttribute = PickedAttributeSimpler<'Moon'>; //This is type 'BN' as expected
type ExampleGenericPickedAttribute<T extends 'Moon'> = PickedAttributeSimpler<T>; //This is DRYObjectModelMap[T]['visibleStarsCount']
type ExampleGenericInstanceOfPickedAttribute = ExampleGenericPickedAttribute<'Moon'>; //This is BN as expected
type HoverToSeeHowTypeShouldResolve<T extends 'Moon'> = DRYObjectModelMap[T]['visibleStarsCount'];
type HoverToSeeHowTypeShouldResolveInstance = HoverToSeeHowTypeShouldResolve<'Moon'>; //This is BN as expected

Well, typescript is a little bit wierd sometimes.好吧,typescript 有时有点奇怪。

If you use a mapped type of a Union to look up the type value it's working.如果您使用 Union 的映射类型来查找它正在工作的类型值。 It would also work with just "Moon", btw.顺便说一句,它也适用于“月亮”。 playground 操场

type Union = "Moon"|"Planet"
type Mapped = {[K in Union]:PickedAttributeSimpler<K>}

//The error disappears when the definition below rather than above is used.
//type PickedAttributeSimpler<T extends 'Moon'> = DRYObjectModelMap['Moon']['visibleStarsCount'];
function genericFunction<
    T extends Union, //union type that includes | 'Planet' omitted for simpler example.
    //Omitting the generic type parameter and hard-coding it into each usage below fixes the error,
    //but the real version of the function not oversimplified for the example needs to be generic.
>(
    PlanetaryBodyClass: T,
) : Mapped[T] { //Same if using DRYObjectModelMap[T]['visibleStarsCount']
    return new BN(42); //No error
}

暂无
暂无

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

相关问题 打字稿泛型。 无法将B的实例传递给参数a:T,其中T扩展了B。为什么? - Typescript generics. Can't pass instance of B to a argument a: T where T extends B. Why? 为什么它不使用“<event> “ 代替 ”<t extends event> “?</t></event> - Why it doesn't use "<Event>" instead of "<T extends Event>"? 如何限制typescript中add(a: T, b: T):T的泛型T,使其要么a和b为字符串,要么a和b为数字? - How to restrict the generic T of add (a: T, b: T):T in typescript so that it is either a and b are strings, or a and b are numbers? 为什么`T extends &quot;A&quot;`和`S extends &quot;A&quot;`没有重叠? - Why `T extends "A"` and `S extends "A"` have no overlap? 为什么 [number | 字符串] 扩展 [数字] | [字符串] 在 typescript 中? - Why doesn't [number | string] extends [number] | [string] in typescript? FUNC <T extends IComponent> ():T [] =&gt; IComponent [] - 不分配给T. - Func<T extends IComponent>( ): T[] => IComponent[] - doesn't assign to T TypeScript:为什么我不能分配类型为 { a: “a”, b: “b” } 的对象的有效字段 - TypeScript: Why can't I assign a valid field of an object with type { a: “a”, b: “b” } Firestore,其中'array-contains'查询不适用于引用 - Firestore where 'array-contains' query doesn't work with references 为什么表现<T extends Record<number, X> &gt; 和<T extends Record<string, X> &gt; T[keyof T] 有什么不同? - Why behave <T extends Record<number, X>> and <T extends Record<string, X>> differently for T[keyof T]? 打字稿:无法让泛型与扩展一起使用 - Typescript: can't get generics to work with extends
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM