我正在创建一个基于代理的系统来拦截对某个模型的所有读/写。但是,我遇到了创建系统所代表的inerface类型的问题。
系统将在读取或写入模型时,使用proxy拦截读/写并相应地创建新代理,以便能够拦截所有将来的读/写操作。根(我们称之为root
)代理被强制转换为代理所代表的类型。例如,这可能是:
interface Root {
foo: string;
bar: Map<OtherType>;
}
,其中
interface Map<T> {
[key: string]: T | T[] | (obj: T) => string;
insert: (obj: T) => string;
asArray: T[];
}
问题在于界面Map<T>
。我希望实际地图的类型只是T
。我认为问题是当root.bar[x]
请求x = insert
时,返回对象的类型为(obj: T) => string
。但是,地图中的所有键都是长度为8的字符串。我真正喜欢的是这样的:
type ID = string[8]
interface Map<T> {
[key: ID]: T
insert: (obj: T) => string;
asArray: T[];
}
我无法弄清楚如何:
我已经查看了typescript声明文件,其中定义了Object类型。在这个定义中,他们似乎完全省略了地图。这是在编译器/ linter本身实现的吗?
有没有人知道修复或解决方法来实现这样的目标?我认为每次进行地图查找时必须转换为类型T是非常难看的。
答案 0 :(得分:1)
首先,您使用单词Map
是有问题的,因为正如@TJCrowder所提到的那样,means something else这个词。我将在后面的内容中调用您的类型MyMap
。
TypeScript不会(当前从v2.5开始)识别与8个字符的字符串对应的string
的任何子类型。你可以这样做:
type Id = string & { length: 8 };
将Id
类型定义为string
的特定类型,但TypeScript不够聪明,无法理解string
何时为Id
(因此它永远不会自动将string
类型缩小为Id
),并且不允许您使用Id
作为对象的索引类型。如果您想将string
变成Id
,则必须使用类型断言:
function toId(str: string): Id {
if (str.length !== 8)
throw new Error("an Id must have exactly 8 characters");
return str as Id;
}
这对你来说可能不值得。
由于TypeScript无法将Id
识别为索引类型,因此无法按照您希望的方式键入MyMap<T>
:索引为string
,"insert"
也是"asArray"
1}}和Id
。最适合TypeScript的解决方法(与TypeScript类型系统配合使用并让您获益)是将interface TSFriendlyMyMap<T> {
props: { [key: string]: T } // can't do key: Id
insert: (obj: T) => Id;
asArray: T[];
}
- 索引属性移动到其自己的对象。例如:
props
在这种情况下,您可以使用declare const reMap: TSFriendlyMyMap<RegExp>;
const id: Id = reMap.insert(/abc/);
const arr: RegExp[] = reMap.asArray;
const regExp: RegExp = reMap.props[id] // instead of reMap[id]
来保存属性。使用地图会有所不同:
MyMap
但对我来说似乎并不差。
您可以做的另一件事是使用function overloads使type CallableMyMap<T> = {
(k: 'insert'): (obj: T) => Id
(k: 'asArray'): T[];
(k: Id): T
}
成为可调用的函数而不是可索引的对象:
Id
这更符合您的需求,因为TypeScript知道T
会产生"insert"
,而"asArray"
和declare const reMap: CallableMyMap<RegExp>;
const id: Id = reMap('insert')(/abc/);
const arr: RegExp[] = reMap('asArray');
const regExp: RegExp = reMap(id);
则不会:
TSFriendlyMyMap
但你正在使用函数调用,这有点奇怪。
我建议CallableMyMap
超过Id
,而string
是否比谨慎{{1}}更可取。但这取决于你。无论如何,希望有所帮助;祝你好运!