没有specific输入以这种方式工作的 TypeScript;如果你静态地知道键列表K
in players
, (say "uniquePlayerId1" | "uniquePlayerId2"
)对于任何特定的键选择players
,那么你可以定义一个特定的类型PlayersType<K>
就它而言就像
type PlayersType<K extends string> =
{ players: Record<K, { isTargeting: K }> }
但既然你不知道K
提前,你想定义SomePayersType
这等于PlayersType<K>
for some K
。这种类型称为存在量化泛型并且,尽管已被要求(参见微软/TypeScript#14466),TypeScript 不直接支持此类类型。与大多数具有泛型的语言一样,TypeScript 仅具有普遍量化通用类型。如果我们有存在量化的泛型,你可能会说:
// not valid TypeScript, don't try this:
type SomePlayersType = <exists K extends string> PlayersType<K>
但现在你还不能。有多种方法可以用通用类型来模拟存在类型,但它们很麻烦,并且不一定适合您的用例。
相反,你可以做的是采取PlayersType<K>
并创建一个通用辅助身份函数来推断K
从传入的值中为您提供。它可能看起来像这样:
const asPlayers = <K extends string>(
obj: PlayersType<K>
) => obj;
但不幸的是,这并不完全按照你想要的方式工作;编译器实际上推断K
来自isTargeting
值而不是来自键players
,这意味着它将拒绝有效的参数:
const players = asPlayers({
players: {
uniquePlayerId1: {
isTargeting: 'uniquePlayerId2' // error?!
},
uniquePlayerId2: {
isTargeting: 'uniquePlayerId2'
},
}
})
我们想告诉编译器不要推断K
from isTargeting
;也就是说,使isTargeting
a 非推理类型参数用法 of K
,按照要求微软/TypeScript#14829。有不同的方法可以实现这种行为;在这种情况下,我们只需添加一个新的类型参数L
这是受约束的 to K
,并使用K
对于按键和L
for isTargeting
:
const asPlayers = <K extends string, L extends K>(
obj: { players: Record<K, { isTargeting: L }> }
): PlayersType<K> => obj;
现在我们来使用它:
const players = asPlayers({
players: {
uniquePlayerId1: {
isTargeting: 'uniquePlayerId2'
},
uniquePlayerId2: {
isTargeting: 'uniquePlayerId1'
},
}
})
/* const players: PlayersType<"uniquePlayerId1" | "uniquePlayerId2"> */
看起来不错;编译器推断players
属于类型PlayersTaype<"uniquePlayerId1" | "uniquePlayerId2">
。让我们看看如果我们传入一些不好的东西会发生什么:
const badPlayers = asPlayers({
players: {
uniquePlayerId1: {
isTargeting: "oopsie" // error
// Type '"oopsie"' is not assignable to
// type '"uniquePlayerId1" | "uniquePlayerId2"'
},
uniquePlayerId2: {
isTargeting: 'uniquePlayerId1'
}
}
})
在这里我们看到了所需的编译器错误;因为没有名为的键"oopsie"
, the isTargeting
属性无法命名"oopsie"
.
所以就这样吧。而不是有具体的SomePlayersType
类型,你有一个PlayersType<K>
输入您推断的位置K
从价值。这是存在主义类型的“半途”,可能足以满足您的需求。您可能会发现自己携带了这个额外的类型参数,所以也许您想使用更简单的东西,例如PlayersType<string>
您控制的内部代码,并且仅强制执行以下推理K
对于不太可信的代码:
function publicFacingCode<K extends string>(players: PlayersType<K>) {
innerLibraryCode(players);
}
function innerLibraryCode(players: PlayersType<string>) {
}
Playground 代码链接