我想找到一种类型的地图,其中键eq为其值的属性,例如:
type IndexMap<I extends keyof V, V extends { [K in I]: string }> = {
[P in V[I]]: V
}
const test: IndexMap<'prop', { ok: number, prop: string }> = {
someVal: {
ok: 1,
prop: 'someVal' // Currently, can be violated without compile error.
}
}
然后,我可以在通用约束中使用它,例如:
class Service<T extends IndexMap<'name', CustomizedPreDefinedApiModel>> {
// T is limited to IndexMap. Can be used to type check in service methods.
}
任何实现方式?
答案 0 :(得分:0)
您的类型定义完全可以,但是我们需要了解TS中类型级别和值级别之间的区别。考虑:
type TypeA = IndexMap<'prop', { ok: number, prop: string }>
// is evaluated to:
type TypeA = {
[x: string]: {
ok: number;
prop: string;
};
}
请注意,我们的键x
的类型等于prop
键值的类型。因此,一切在类型级别上都是正确的。我们声明prop: string
和编译器确实对此进行监视。因此,将数字放在prop中将显示编译错误。
是的,但这不是我们想要的,但是定义的变量类型不会对值进行任何推断。考虑:
const test: IndexMap<'prop', { ok: number, prop: string }> = {
someVal: {
ok: 1,
prop: 'someVal' // Currently, can be violated without compile error.
}
}
我们静态地定义类型,我们说prop是一个字符串,因此右边的值确实是该类型的有效成员。但是无法通过值更改此类型。推断类型参数的唯一方法是通过函数参数,稍后我将再次介绍。
如果我们使用更严格的类型,结果将有所不同。
type TypeB = IndexMap<'prop', { ok: number, prop: 'val'}>;
// it evaluates into
type TypeB = {
val: { // correctly narrowed key
ok: number;
prop: "val"; // correctly narrowed type
};
}
因此,当您给它一个prop类型时,原始类型将按预期工作,并创建所需的结构。但是,如何将值级别与类型级别连接起来以免放置此类特定类型,我认为这就是您想要的。
首先对您的类型进行细微更改:
type IndexMap<I extends keyof V, V extends { [K in I]: PropertyKey }> = {
[P in V[I]]: V
}
我更改了PropertyKey
中的字符串,这种类型表示我们需要在给定的密钥I
处具有可用作密钥的值(字符串|数字|符号)。
现在如何在不直接指定类型的情况下使值适合给定类型。我们可以使值构造函数。考虑:
const makeTest = <I extends keyof V, V extends { [K in I]: VK }, VK extends PropertyKey>(a:I, b:V) => {
return {
[b[a]]: {
...b
}
} as IndexMap<I, V>
}
const test3 = makeTest('a', { 'a': 'value', b: 1 });
const test4 = makeTest('b', { 'a': 'value', b: 1 });
test3
和test4
均为IndexMap
类型的正确值。我们不需要显式定义类型,因为我们可以从参数中派生类型,也可以指定类型并适当缩小类型。
总结一下。当说某类给定的类型为a: T
时,我们将值a
约束为类型T
,但我们在值a
中输入的内容不会影响类型{{1} }。