[英]TypeScript interface mapped property generic type
I'm trying to make this work我正在努力使这项工作
interface ObjectPool<Ids, T> {
pool: {
[K in Ids]: T<K>;
};
};
interface Player<Id> {
id: Id;
}
let playerPool: ObjectPool<0 | 1 | 2, Player>;
so that以便
playerPool[0].id === 0;
playerPool[1].id === 1;
playerPool[2].id === 2;
// playerPool[3] error
but typescript says I need a generic parameter at Player
in let playerPool: ObjectPool<0 | 1 | 2, Player>;
但是 typescript 说我需要一个通用参数在Player
中的let playerPool: ObjectPool<0 | 1 | 2, Player>;
let playerPool: ObjectPool<0 | 1 | 2, Player>;
so I tried let playerPool: ObjectPool<0 | 1 | 2, Player<_>>;
所以我试着let playerPool: ObjectPool<0 | 1 | 2, Player<_>>;
let playerPool: ObjectPool<0 | 1 | 2, Player<_>>;
but that doesn't work too但这也行不通
If you write T<K>
, then T
needs to be some particular type operation (eg, type T<K> =...
or interface T<K> {...
or class T<K>...
).如果您编写T<K>
,则T
需要是某种特定类型的操作(例如, type T<K> =...
或interface T<K> {...
或class T<K>...
)。 There's no way to write T<K>
where T
is a generic type parameter.没有办法编写T<K>
,其中T
是泛型类型参数。 That would require so-called higher-kinded types , of the sort requested in microsoft/TypeScript#1213 , and TypeScript has no direct support for that.这将需要microsoft/TypeScript#1213中要求的所谓的更高种类的类型,而 TypeScript 对此没有直接支持。
You could step back and try to think of exactly what you want to do, and if there's any way to represent it without needing higher kinded types.你可以退后一步,试着想一想你想要做什么,以及是否有任何方法可以在不需要更高种类的类型的情况下表示它。 If all you want is for ObjectPool<P, T>
to have all property keys in P
, and for each such key K
, you want the property value to have an id
property equal K
in addition to some other properties specified by T
, then you can separate out the id
part in the definition so that T
is just a regular type.如果您想要的只是ObjectPool<P, T>
拥有P
中的所有属性键,并且对于每个这样的键K
,除了T
指定的其他一些属性之外,您希望属性值具有等于K
的id
属性,那么您可以在定义中分离出id
部分,以便T
只是一个常规类型。 For example:例如:
type ObjectPool<P extends PropertyKey, T> =
{ [K in P]: { id: K } & T };
Now you could define Player
without making it generic:现在您可以定义Player
而不使其通用:
interface Player {
id: number, // <-- you don't necessarily need this anymore
name: string,
}
And now an ObjectPool<0 | 1 | 2, Player>
现在是一个ObjectPool<0 | 1 | 2, Player>
ObjectPool<0 | 1 | 2, Player>
ObjectPool<0 | 1 | 2, Player>
should behave as desired: ObjectPool<0 | 1 | 2, Player>
的行为应符合要求:
function processPlayerPool(playerPool: ObjectPool<0 | 1 | 2, Player>) {
playerPool[0].id === 0;
playerPool[1].id === 1;
playerPool[2].id === 2;
playerPool[2].name;
playerPool[3] // error
}
You can then define other types to use instead of Player
and use them too:然后,您可以定义其他类型来代替Player
并使用它们:
interface Wall {
location: string
orientation: string
}
function processSmallWallPool(wallPool: ObjectPool<0 | 1, Wall>) {
wallPool[0].location // okay
wallPool[0].id === 0; // okay
wallPool[1].orientation // okay
wallPool[2] // error
}
You mentioned in a comment that you have 2,000 Wall
objects in the pool.您在评论中提到池中有 2,000 个Wall
对象。 That's a lot of elements to put in a union , but sure, you could do it (code generation is going to be easier than trying to convince the compiler to compute it):这是要放入union的很多元素,但可以肯定的是,您可以做到(代码生成将比试图说服编译器计算它更容易):
// console.log("type WallIds = " + Array.from({ length: 2000 }, (_, i) => i).join(" | "));
type WallIds = 0 | 1 | 2 | 3 | 4 | // ✂ SNIP!
| 1995 | 1996 | 1997 | 1998 | 1999
And then ObjectPool<WallIds, Wall>
will also behave as desired:然后ObjectPool<WallIds, Wall>
也将按需要运行:
function processWallPool(wallPool: ObjectPool<WallIds, Wall>) {
wallPool[214].location
wallPool[100].id // 100
wallPool[1954].orientation
wallPool[2021] // error
}
Please note though that the compiler really can't do much analysis on a union of numeric literals.请注意,尽管编译器确实无法对数字文字的联合进行太多分析。 You might have more trouble than you expected with this.您可能会遇到比您预期的更多的麻烦。 If you try to loop over the elements of wallPool
with a numeric index i
, the compiler will complain:如果您尝试使用数字索引i
wallPool
的元素,编译器会报错:
for (let i = 0; i < 2000; i++) {
wallPool[i] // error!
//~~~~~~~~~~~
// No index signature with a parameter of type 'number'
// was found on type 'ObjectPool<WallIds, Wall>'
}
It has no idea that i
is guaranteed to be a value of type WallIds
in that loop.它不知道i
在该循环中被保证是WallIds
类型的值。 It infers number
, and you can't index into wallPool
with any old number
.它推断number
,并且您不能使用任何旧number
索引到wallPool
。 It needs to be a value of the WallIds
union.它必须是WallIds
联合的值。 You could assert that i
is a WallIds
:您可以断言i
是WallIds
:
for (let i = 0 as WallIds; i < 20000; i++) {
wallPool[i] // no error, but, 20000 is an oopsie
}
but, as shown above, you run into the problem that the compiler can't understand that i++
might make i
no longer a valid WallIds
, as explained in this comment of microsoft/TypeScript#14745 .但是,如上所示,您会遇到编译器无法理解i++
可能使i
不再是有效WallIds
的问题,如microsoft/TypeScript#14745的此评论中所述。
If you're only ever going to be indexing into WallPool
with a numeric literal value, like wallPool[123]
or wallPool[1987]
, then that's fine.如果您只打算使用数字文字值(如wallPool[123]
或wallPool[1987]
)对WallPool
进行索引,那很好。 But as soon as you start storing and manipulating indices of type number
, you will likely hit a roadblock with this approach.但是,一旦您开始存储和操作number
类型的索引,您可能会遇到这种方法的障碍。 It might still be worth it to you, but it's important to be aware of it.它对您来说可能仍然值得,但重要的是要意识到这一点。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.