首页 > 解决方案 > 如何在 Typescript 中使用函数增强接口?

问题描述

我有一个看起来像这样的界面:

export interface Attribute {
  name: string;
}

有了这个,我可以使用这个语法来创建一个Attribute

const attr: Attribute = {
  name: "foo"
};

我的问题是我不想使用类,因为它们笨拙且不够灵活,但我想继续使用上面的语法来创建对象,因为它非常简洁。我读过可以进行声明合并,所以我尝试这样做以向该接口添加一个新函数:

import { Attribute } from "./Attribute";

declare module "./Attribute" {
  interface Attribute {
    matches(other: Attribute): boolean;
  }
}

Attribute.prototype.matches = function (other: Attribute): boolean {
  return this.name === other.name;
};

这里的问题是prototype由于接口在运行时不可用,因此无法增加。如果我将Attribute接口与一个类合并:

export class Attribute {}

export interface Attribute {
  name: string;
}

有一个原型并且增强编译但我仍然不能使用它:

import { Attribute } from "./Attribute";
import "./AttributeAugments";

const attr: Attribute = {
  name: "foo"
};
// ^^^--- Property 'matches' is missing in type '{ id: number; name: string; }' but required in type 'Attribute'

console.log(attr);

有没有办法以某种方式做到这一点?我的目标是能够做attr.matches(other)而不是不得不做matches(attr, other)。这样我就可以像做函数链接attr.a().b().c()而不是必须做c(b(a(attr))).

标签: typescript

解决方案


从您的代码来看,Attribute是一个接口。一个类型或接口在 JavaScript 中编译为空;因此,你不能在你的问题中这样做。

declare module "./Attribute" {
  interface Attribute {
    matches(other: Attribute): boolean;
  }
}

// No such "Attribute" variable!
Attribute.prototype.matches = function (other: Attribute): boolean {
  return this.name === other.name;
};

const attr: Attribute = {
  name: "foo"
};
attr.matches(); // error, attr's prototype is Object, not Attribute
// because there is no constructor function or class Attribute

有一些方法可以解决您的问题。

matches(1)每次创建类型变量时始终包含方法Attribute

export function matches(this: Attribute, other: Attribute): boolean {
  return this.name === other.name;
};
const attr: Attribute = {
  name: "foo",
  matches,
}; // works

(2) 使用Attribute类但简化其实例化:

type ExcludeMethods<T extends object> = Pick<
  T,
  {
    [x in keyof T]: T[x] extends Function ? never : x;
  }[keyof T]
>; // without this type, "matches" method is required in the object literal!

class Attribute {
  constructor(init: ExcludeMethods<Attribute>) {
    Object.assign(this, init);
  }

  name: string;

  matches(other: Attribute): boolean {
    return this.name === other.name;
  }
}

const attr = new Attribute({ name: "foo" }); // works
const attr2 = new Attribute({}); // error, name is required

如果name: string属性导致编译错误,请添加非空断言(使其变为name!: string),因为我们非常确定非函数字段必须存在于Object.assignto 中this


推荐阅读