通过外部函数缩小TypeScript类型?

时间:2018-08-17 19:01:27

标签: typescript

我正在尝试编写一个外部函数以使用TypeScript缩小对象的类型,但是,一旦我退出逻辑,TypeScript似乎就失去了线程。我有以下代码:

if (element instanceof HTMLInputElement && element.checked) {
  // do something
}

但是,我想进行第一次结帐并向我们发送通知,告知发生问题:

function assertType(element, expectedType) {
  if (element instanceof expectedType) { return true; }

  notify(`element ${element} wasn't expected type ${expectedType}`);

  return false;
}

if (assertType(element, HTMLInputElement) && element.checked) {
  // do something
}

TypeScript对第一个感到满意,但第二个却抱怨:

error TS2339: Property 'checked' does not exist on type 'HTMLElement'.

即使我将其减少到最低限度,它仍然会失败:

function assertType(element, expectedType) {
  return element instanceof expectedType;
}

有没有一种方法可以编写assertType函数,以便TypeScript可以缩小类型?我试图避免进行类型转换以确保类型与基础html一致,但我想我可以在紧要关头使用它。甚至可能像这样:

if (assertType(element, HTMLInputElement) && (element as HTMLInputElement).checked) {
  // do something
}

1 个答案:

答案 0 :(得分:3)

这是user-defined type guardsgenerics的工作,因此它可以应用于任何类型。这是一种使您的assertType函数的方法:

function assertType<T>(element: any, expectedType: new(...args: any[])=>T): element is T {
  return element instanceof expectedType;
}

这个想法是expectedType应该是某个通用类型T的构造函数,并且它将返回是否element is T。让我们看看它是否有效,键入system wise:

declare const element: Element;

if (assertType(element, HTMLInputElement) && (element.checked)) {
  // do something
}

没有错误。希望能有所帮助。祝你好运!


更新

  

如果要使其更加通用并允许expectedType成为与instanceof匹配的构造函数或与typeof匹配的字符串怎么办?

要执行一个函数似乎有点困难,但是如果尝试尝试,我可能会使用overloadslookup types,如下所示:

type TypeofMapping = {
  string: string,
  number: number,
  boolean: boolean,
  symbol: symbol,
  undefined: undefined,
  object: object,
  function: Function
}

function assertType<T>(element: any, expectedType: new (...args: any[]) => T): element is T;
function assertType<K extends keyof TypeofMapping>(element: any, expectedType: K): element is TypeofMapping[K];
function assertType(element: any, expectedType: new (...args: any[]) => any | keyof TypeofMapping): boolean {
  return (typeof expectedType === "string") ? typeof element === expectedType : element instanceof expectedType;
}

重点是让代码选择两个重载之一。第一个与以前一样,但是第二个将expectedType约束为运行时typeof运算符返回的字符串之一,并通过TypeofMapping将该字符串映射为类型。让我们再试一次:

declare const element: Element;

if (assertType(element, HTMLInputElement) && (element.checked)) {
  // do something
}

declare const otherThing: string | number;
if (assertType(otherThing, "string") && otherThing.charAt(0) === 'a') {
  // do something else
}

似乎也可以工作(尽管尚未在运行时进行测试)。不过,我自己还是会回避这种分裂人格的功能。由你决定。再次祝你好运!