类型脚本实用程序记录和部分不能与受约束的泛型一起使用

编程入门 行业动态 更新时间:2024-10-26 13:19:57
本文介绍了类型脚本实用程序记录和部分不能与受约束的泛型一起使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我需要基于受约束的泛型创建新类型。新类型应该与泛型具有相同的键,使它们成为可选的,并将它们映射到一个数字。

我的第一个方法是使用Partial<Record<keyof Entity, number>>,但由于某种原因导致错误Type [...] is not assignable to type 'Partial<Record<keyof Entity, number>>'.。

所以我求助于手动编写类型映射{ [K in keyof Entity]?: number },这似乎很管用。

interface Person { firstName: string; lastName: string; } export class SomeClass<Entity extends Person> { protected getFilter(): void { let prio1: { [K in keyof Entity]?: number }; prio1 = { firstName: 1 }; // Type '{ firstName: 2; }' is not assignable to type 'Partial<Record<keyof Entity, number>>'. let prio2: Partial<Record<keyof Entity, number>>; prio2 = { firstName: 2 }; } }

谁能告诉我这两种表示法之间的区别是什么,为什么内置的打字实用程序不能工作?

该代码片段也可在TypeScrip Platform上找到:see here。

我使用的资源:Records、Mapped Types、Generics

更新:

我试图分解TypeScrip在内部的作用,使用官方的类型定义一步一步地对其进行美化:

/** * Make all properties in T optional */ type Partial<T> = { [P in keyof T]?: T[P] }; /** * Construct a type with a set of properties K of type T */ type Record<K extends keyof any, T> = { [P in K]: T }; class SomeClass<Entity extends Person> { getFilter(): void { let prio1: { [K in keyof Entity]?: number }; prio1 = { firstName: 1 }; let p1: Partial<Record<keyof Entity, number>>; let p2: Partial<{ [P in keyof Entity]: number }>; let p3: { [P2 in keyof { [P1 in keyof Entity]: number }]?: { [P1 in keyof Entity]: number }[P2] }; let p4: { [P2 in keyof Entity]?: { [P1 in keyof Entity]: number }[P2] }; let p5: { [P2 in keyof Entity]?: number }; p1 = { firstName: 3 }; // error p2 = { firstName: 3 }; // error p3 = { firstName: 3 }; // error p4 = { firstName: 3 }; // error p5 = { firstName: 3 }; // works } }

TypeScrip似乎无法将{ [P1 in keyof Entity]: number }[P2]与P2 in keyof Entity解析为number。

推荐答案

首先请看这个answer。它应该会为您提供一些背景信息。

将Entity视为Person的子类型。它不仅有Person道具,还可能有其他道具。

请参见此示例:

interface Person { firstName: string; lastName: string; } type User = { premium: true } & Person

User是Person的子类型,也就是扩展了Person。

让我们创建用户对象:

interface Person { firstName: string; lastName: string; } type User = { premium: true } & Person let user: Record<keyof User, number> = { firstName: 1, lastName: 2, premium: 3 }

因此,我们的Partial用户可能有很多状态:

// just an alias type PartiaUser = Partial<Record<keyof User, number>> let partialUser1: PartiaUser = {} let partialUser2: PartiaUser = { firstName: 1 } let partialUser3: PartiaUser = { firstName: 1, lastName: 2, } let partialUser4: PartiaUser = { lastName: 2, } let partialUser5: PartiaUser = { lastName: 2, premium: 3, }

参见partialUser5它是一个没有firstName属性的对象。

让我们回到您的示例:

interface Person { firstName: string; lastName: string; } export class SomeClass<Entity extends Person> { getFilter(): void { let prio1: { [K in keyof Entity]?: number }; prio1 = { firstName: 1 }; let prio2: Partial<Record<keyof Entity, number>>; prio2 = { firstName: 2 }; } }

prior1之所以有效,是因为在这种情况下,prior1肯定具有可选的firstName属性,因为Entity具有Person的所有键。

prior2非常有趣。

我认为问题出在Java脚本的动态性上。这只是我的观点。

请参见此示例:

type User = { firstName: string; lastName: string; premium: true } & string type IsExtends<T> = T extends Person ? true : false type Test = IsExtends<User>

问题是上述User类型仍然扩展Person。

interface Person { firstName: string; lastName: string; } type User = { firstName: string; lastName: string; premium: true } & string export class SomeClass<Entity extends Person> { getFilter(): void { let prio1: { [K in keyof Entity]?: number }; prio1 = { firstName: 1 }; let prio2: Partial<Record<keyof Entity, number>>; prio2 = { firstName: 2 }; } } const result = new SomeClass<User>() // no error

事实上,User是一个带有一些静态属性的字符串。

它在类型系统中可表示,但在运行时不可表示。

让我们用一个小例子进行测试:

type PartialUser = Partial<Record<keyof User, number>> declare var partialUser: PartialUser partialUser = { firstName: 2 } // error 在这种情况下,问题出在toString方法中。请看一下toString是如何在string原语中实现的:

// toString: () => string; type ToString = Pick<{ [Prop in keyof string]: string[Prop] }, 'toString'>

您可能已经注意到,toString是一个函数。我的意思是toString的值是一个函数。但是,Record<keyof User, number>预期每个值都是number。

让我们再来看看我们的示例:

interface Person { firstName: string; lastName: string; } type User = { firstName: string; lastName: string; premium: true; toString: number; } export class SomeClass<Entity extends Person> { getFilter(): void { let prio1: { [K in keyof Entity]?: number }; prio1 = { firstName: 1 }; // added User for experimenting with prio2 = { firstName: 2, toString: 42 } let prio2: Partial<Record<keyof User, number>>; prio2 = { firstName: 2 }; } } const result = new SomeClass<User>() // no error

为清楚起见,我已显式添加了toString属性。

prio1 = { firstName: 1 }-之所以有效,是因为我们创建了新类型,其中每个值都是number。

由于我们已覆盖内置方法,因此TypeScrip声明此方法与默认方法不兼容toString。

尝试使用prio2 = { firstName: 2, toString: 42 }。您将看到它将修复错误。

默认情况下,TS期望每个对象都有toString作为方法,这是预期的行为。但如果您覆盖它-它会使TS不高兴

我希望现在清楚您为什么会出错。

extends-不表示-equal

Playground

Related github issue

更多推荐

类型脚本实用程序记录和部分不能与受约束的泛型一起使用

本文发布于:2023-11-15 17:04:51,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1596331.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:能与   脚本   实用程序   类型

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!