如何使TypeScript类型保护对编译产生影响?

如何使TypeScript类型保护对编译产生影响?,typescript,types,Typescript,Types,我创建了一个名为KVMap的类,该类继承自Map,但类型完全不同 class KVMap<T extends object> extends Map<keyof T, T[keyof T]> { ... 所有类型都可以选择未定义,您可以在键上调用。清除或。删除 到目前为止一切正常 Map的原始类型有一个问题:调用.has意味着您仍然可能得到编译器错误。 例如: 这是因为.has只返回一个布尔值。 我试图解决这个问题 我在继承的类中重新定义了。has,如下所示:

我创建了一个名为
KVMap
的类,该类继承自
Map
,但类型完全不同

class KVMap<T extends object> extends Map<keyof T, T[keyof T]> {
    ...
所有类型都可以选择
未定义
,您可以在键上调用
。清除
。删除

到目前为止一切正常

Map
的原始类型有一个问题:调用
.has
意味着您仍然可能得到编译器错误。 例如:

这是因为
.has
只返回一个布尔值。 我试图解决这个问题

我在继承的类中重新定义了
。has
,如下所示:

has<K extends keyof T>(key: K): this is this & {
    get(key: K): T[K]
};
has(key:K):这是这个&{ get(key:K):T[K] }; 这意味着以后调用
.get
将不再在其类型中包含
| undefined

然而,我认为
this
的原始类型,因为它包含可选的未定义类型,优先于类型guard union

有人知道我该怎么解决这个问题吗


(如果有必要,我可以附加更多的代码)

手册文档中没有明确说明,但是当您创建两个可调用类型的类型时,它生成的类型就像带有两个调用签名的类型,其顺序与它们在交叉点中出现的顺序相同。因此,虽然直观上和在TypeScript中它们通常是,但函数类型的交集不是:

ns.foo()


因此,您的类型guard
this是这个&{get(key:K):T[K]}
的问题在于原始的
this
有一个
get()
类型为
Map[“get”]
的方法,因此生成的交集只是在那之后添加了一个新的重载方法。由于原始的
get()
方法适用于所有
keyof T
输入,因此它将始终处于选中状态,因此您添加的重载方法将完全隐藏:

  interface Tee {
    a: string,
    b: number,
    c: boolean
  }

  const v = new KVMap<Tee>();

  if (v.has("a")) {
    v.get; /* OVERLOADED:
    get(key: "a" | "b" | "c"): string | number | boolean | undefined;
    get(key: "a"): string
    */

    v.get("a").toUpperCase(); // error!
万岁


但请注意,只有将单个字符串文本类型的值传递给
has()
时,这种类型保护才有意义。如果
has()
的参数是联合类型,那么您将度过一段不愉快的时光,尽管编译器向开发人员保证这是不可能的,但在运行时仍然可能会出现
未定义的

  // problem
  const bc = Math.random() < 0.5 ? "b" : "c";
  const cb = (bc === "b") ? "c" : "b";
  if (v.has(bc)) {
    v.get(cb).toString(); // compiles fine, but error at runtime!
  }
//问题
常量bc=Math.random()<0.5?“b”:“c”;
常数cb=(bc=“b”)?“c”:“b”;
如果(v.has(bc)){
v、 get(cb).toString();//编译很好,但在运行时出错!
}
所以要小心



你在说什么“断言”?我没有看到任何类型断言here@jcalz您是对的,更新为使用“Type-Guard”代替断言;我忘了它的名字。我快死了,TS是一种有趣的语言。从什么时候开始,秩序在其他语言中起作用了!?交换订单,效果很好。谢谢你指出这一点。
type FN = {foo(): number};
type FS = {foo(): string};

declare const ns: FN & FS;
ns.foo().toFixed(); // okay
ns.foo().toUpperCase(); // error 

declare const sn: FS & FN;
sn.foo().toFixed(); // error
sn.foo().toUpperCase(); // okay
  interface Tee {
    a: string,
    b: number,
    c: boolean
  }

  const v = new KVMap<Tee>();

  if (v.has("a")) {
    v.get; /* OVERLOADED:
    get(key: "a" | "b" | "c"): string | number | boolean | undefined;
    get(key: "a"): string
    */

    v.get("a").toUpperCase(); // error!
  class KVMap<T extends object> extends Map<keyof T, T[keyof T]> {
    declare readonly has: <K extends keyof T>(key: K) => this is {
      get(key: K): T[K]
    } & this;
  }

  const v = new KVMap<Tee>();
  if (v.has("a")) {
    v.get("a").toUpperCase(); // okay now
  }
  // problem
  const bc = Math.random() < 0.5 ? "b" : "c";
  const cb = (bc === "b") ? "c" : "b";
  if (v.has(bc)) {
    v.get(cb).toString(); // compiles fine, but error at runtime!
  }