类型脚本实用程序记录和部分不能与受约束的泛型一起使用 [英] Typescript utilities 'Record' and 'Partial' don't work with a constrained generic
问题描述
我需要基于受约束的泛型创建新类型。新类型应该与泛型具有相同的键,使它们成为可选的,并将它们映射到一个数字。
我的第一个方法是使用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
这篇关于类型脚本实用程序记录和部分不能与受约束的泛型一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!