打字稿

时间:2017-10-02 13:16:01

标签: typescript types proxy

我正在创建一个基于代理的系统来拦截对某个模型的所有读/写。但是,我遇到了创建系统所代表的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[];
}

我无法弄清楚如何:

  • 创建一个类型ID,字符串长度为8
  • 使用替代字符串类型作为键(typescript将不允许我)

我已经查看了typescript声明文件,其中定义了Object类型。在这个定义中,他们似乎完全省略了地图。这是在编译器/ linter本身实现的吗?

有没有人知道修复或解决方法来实现这样的目标?我认为每次进行地图查找时必须转换为类型T是非常难看的。

1 个答案:

答案 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}}更可取。但这取决于你。无论如何,希望有所帮助;祝你好运!