使用 Typescript 检查接口类型

IT技术 javascript typescript interface
2021-01-27 11:00:24

这个问题是与 TypeScript 的类类型检查的直接类比

我需要在运行时找出 any 类型的变量是否实现了接口。这是我的代码:

interface A{
    member:string;
}

var a:any={member:"foobar"};

if(a instanceof A) alert(a.member);

如果您在typescript操场中输入此代码,最后一行将被标记为错误,“名称 A 在当前范围内不存在”。但事实并非如此,该名称确实存在于当前作用域中。我什至可以将变量声明更改为var a:A={member:"foobar"};没有编辑器的抱怨。在浏览网页并在 SO 上找到另一个问题后,我将接口更改为类,但随后我无法使用对象文字来创建实例。

我想知道类型 A 是如何消失的,但查看生成的 javascript 可以解释这个问题:

var a = {
    member: "foobar"
};
if(a instanceof A) {
    alert(a.member);
}

没有将 A 表示为接口,因此不可能进行运行时类型检查。

我知道 javascript 作为一种动态语言没有接口的概念。有没有办法对接口进行类型检查?

typescript游乐场的自动完成显示typescript甚至提供了一个方法implements我怎样才能使用它?

6个回答

您可以在没有instanceof关键字的情况下实现您想要的,因为您现在可以编写自定义类型保护:

interface A{
    member:string;
}

function instanceOfA(object: any): object is A {
    return 'member' in object;
}

var a:any={member:"foobar"};

if (instanceOfA(a)) {
    alert(a.member);
}

会员多

如果您需要检查大量成员以确定某个对象是否与您的类型匹配,则可以改为添加鉴别器。以下是最基本的示例,需要您管理自己的鉴别器……您需要更深入地了解模式以确保避免重复鉴别器。

interface A{
    discriminator: 'I-AM-A';
    member:string;
}

function instanceOfA(object: any): object is A {
    return object.discriminator === 'I-AM-A';
}

var a:any = {discriminator: 'I-AM-A', member:"foobar"};

if (instanceOfA(a)) {
    alert(a.member);
}
如果接口有100个成员,你需要检查所有100个吗?足球吧。
2021-03-14 11:00:24
您可以向对象添加鉴别器,而不是检查所有 100 ...
2021-03-26 11:00:24
@Fenton 也许我对此还不够了解,但是假设您有一个扩展接口 A 的接口 B,您希望isInstanceOfA(instantiatedB)返回 true,但希望isInstanceOfB(instantiatedA)返回 false。对于后者,B 的鉴别器不是必须不是“I-AM-A”吗?
2021-03-27 11:00:24
“没有办法运行时检查接口。” 是的,他们只是出于某种原因还没有实施它。
2021-03-30 11:00:24
这个鉴别器范式(如这里所写)不支持扩展接口。如果检查派生接口是否是基接口的 instanceOf,则派生接口将返回 false。
2021-04-07 11:00:24

在 TypeScript 1.6 中,用户定义的类型保护将完成这项工作。

interface Foo {
    fooProperty: string;
}

interface Bar {
    barProperty: string;
}

function isFoo(object: any): object is Foo {
    return 'fooProperty' in object;
}

let object: Foo | Bar;

if (isFoo(object)) {
    // `object` has type `Foo`.
    object.fooProperty;
} else {
    // `object` has type `Bar`.
    object.barProperty;
}

正如 Joe Yang 所说:从 TypeScript 2.0 开始,您甚至可以利用标记联合类型。

interface Foo {
    type: 'foo';
    fooProperty: string;
}

interface Bar {
    type: 'bar';
    barProperty: number;
}

let object: Foo | Bar;

// You will see errors if `strictNullChecks` is enabled.
if (object.type === 'foo') {
    // object has type `Foo`.
    object.fooProperty;
} else {
    // object has type `Bar`.
    object.barProperty;
}

它也适用switch

@mostruash 是的,答案的后半部分即使编译也不会在运行时工作。
2021-03-25 11:00:24
@lhk 不,没有这样的语句,它更像是一种特殊类型,它告诉如何在条件分支内缩小类型。由于 TypeScript 的“范围”,我相信即使将来也不会有这样的声明。object is type之间的另一个区别object instanceof class是,TypeScript 中的类型是结构性的,它只关心“形状”而不是对象从哪里获得形状:普通对象或类的实例,这无关紧要。
2021-03-29 11:00:24
哦,但是,这必须假设在运行时这些对象将使用type属性创建在这种情况下,它有效。那个例子没有显示这个事实。
2021-04-02 11:00:24
只是为了清除这个答案可能造成的误解:在运行时没有元信息来推断对象类型或其接口。
2021-04-07 11:00:24
这看起来比较好奇。显然有某种元信息可用。为什么用这种类型保护语法公开它。与 isinstanceof 相比,函数旁边的“对象是接口”受哪些约束?更准确地说,您可以在 if 语句中直接使用“对象是接口”吗?但无论如何,非常有趣的语法,来自我的 +1。
2021-04-09 11:00:24

typescript 2.0 引入标记联合

typescript 2.0 功能

interface Square {
    kind: "square";
    size: number;
}

interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}

interface Circle {
    kind: "circle";
    radius: number;
}

type Shape = Square | Rectangle | Circle;

function area(s: Shape) {
    // In the following switch statement, the type of s is narrowed in each case clause
    // according to the value of the discriminant property, thus allowing the other properties
    // of that variant to be accessed without a type assertion.
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.width * s.height;
        case "circle": return Math.PI * s.radius * s.radius;
    }
}
这实际上只是使用了鉴别器。
2021-03-13 11:00:24
我正在使用 2.0 测试版,但标记联合不起作用。<TypeScriptToolsVersion>2.0</TypeScriptToolsVersion>
2021-03-17 11:00:24
并且在创建对象时必须指定其类型?这是不可接受的!
2021-03-31 11:00:24
是的!这岩石!如此干净,我喜欢干净和最简单的可能
2021-04-08 11:00:24
使用夜间构建编译,但智能感知不起作用。它还列出了错误:属性宽度/大小/...不存在于类型 'Square | 矩形 | 在 case 语句中圈出。但它编译。
2021-04-09 11:00:24

用户定义的类型保护怎么样?https://www.typescriptlang.org/docs/handbook/advanced-types.html

interface Bird {
    fly();
    layEggs();
}

interface Fish {
    swim();
    layEggs();
}

function isFish(pet: Fish | Bird): pet is Fish { //magic happens here
    return (<Fish>pet).swim !== undefined;
}

// Both calls to 'swim' and 'fly' are now okay.

if (isFish(pet)) {
    pet.swim();
}
else {
    pet.fly();
}
由于某种原因,这对我不起作用,但(pet as Fish).swim !== undefined;确实如此。
2021-03-30 11:00:24
@Kayz 我猜当你使用时isFish,你的代码并不真正关心对象是否属于任意鱼类别,你更关心你的对象是否支持游泳操作。也许更好的函数名称可能反映了isAquatic诸如此类的情况。这种识别对象类型的方法称为鸭子类型,如果需要,您可以深入了解它。但简而言之,如果一只鸭子会游泳,那么它就是一条鱼,我们有一个命名问题需要解决。en.wikipedia.org/wiki/Duck_typing
2021-03-31 11:00:24
如果“宠物是鱼”,那么通过鱼或鸟的能力有什么意义?这是可怕的可读性!
2021-03-31 11:00:24
当你添加swim();到 Bird时会发生什么,因为你有一只宠物鸭?每只宠物都会被识别为鱼,不是吗?
2021-04-01 11:00:24
这是我最喜欢的答案 - 类似于stackoverflow.com/a/33733258/469777,但没有可能因缩小等原因而中断的魔法字符串。
2021-04-09 11:00:24

现在有可能,我刚刚发布了一个增强版的TypeScript编译器,提供了完整的反射功能。您可以从其元数据对象实例化类,从类构造函数中检索元数据并在运行时检查接口/类。你可以在这里查看

用法示例:

在您的一个typescript文件中,创建一个接口和一个实现它的类,如下所示:

interface MyInterface {
    doSomething(what: string): number;
}

class MyClass implements MyInterface {
    counter = 0;

    doSomething(what: string): number {
        console.log('Doing ' + what);
        return this.counter++;
    }
}

现在让我们打印一些已实现的接口列表。

for (let classInterface of MyClass.getClass().implements) {
    console.log('Implemented interface: ' + classInterface.name)
}

使用 reflect-ts 编译并启动它:

$ node main.js
Implemented interface: MyInterface
Member name: counter - member kind: number
Member name: doSomething - member kind: function

有关Interface元类型的详细信息,请参阅reflection.d.ts

更新: 您可以在此处找到完整的工作示例

实际上,我看到的这种反射特性的主要目的是创建更好的 IoC 框架,就像 Java 世界早已拥有的框架一样(Spring 是第一个也是最重要的一个)。我坚信 TypeScript 可以成为未来最好的开发工具之一,而反射是它真正需要的功能之一。
2021-03-15 11:00:24
这正是概念验证的目的:向人们证明事情是可以完成的。问题指出:“我知道 javascript 作为一种动态语言没有接口的概念。有没有办法对接口进行类型检查?” 答案是:没有没有修改/改进,但如果我们有办法扩展/改进语言和编译器,那就是。问题是:谁来决定这些变化?但这是另一个话题。
2021-03-15 11:00:24
@dudewad 正如在许多其他主题中所说,这是一个临时解决方案。我们正在等待通过转换器实现编译器可扩展性。请参阅官方 TypeScript 存储库中的相关问题。此外,所有广泛采用的强类型语言都有反射,我认为 TypeScript 也应该有。和我一样,许多其他用户也这么认为。
2021-03-28 11:00:24
...呃,那又怎样,我们必须将这些编译器的“增强功能”应用到任何未来的 Typescript 版本中?这实际上是 Typescript 的一个分支,而不是 Typescript 本身,对吧?如果是这样,这不是一个可行的长期解决方案。
2021-04-06 11:00:24
被否决的 cos 我认为这很愚蠢,但后来停顿了一下,看了看你的 github 页面,发现它是最新的,并且有据可查,所以我投票赞成 :-) 我现在仍然无法证明自己现在使用它只是为了implements但想承认你的Promise,不想刻薄:-)
2021-04-08 11:00:24