这个问题直接类似于TypeScript中的类类型检查

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

interface A{
    member:string;
}

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

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

如果您在typescript游乐场中输入这段代码,最后一行将被标记为错误,“名称A不存在于当前作用域”。但事实并非如此,该名称确实存在于当前作用域中。我甚至可以更改变量声明为var a: a ={成员:"foobar"};没有编辑的抱怨。在浏览网页并找到其他问题后,我将接口更改为类,但我不能使用对象字面量来创建实例。

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

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

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

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

typescript游乐场的自动完成显示typescript甚至提供了一个方法实现。我怎么使用它?


当前回答

答案很简单。然而,这种解决方案至少在大约3/4的情况下是可能的(尽管并不总是理想的)。所以,换句话说,这可能与阅读这篇文章的人有关。

假设我有一个非常简单的函数,需要知道参数的接口类型:

const simpleFunction = (canBeTwoInterfaces: interfaceA | interface B) => { 
  // if interfaceA, then return canBeTwoInterfaces.A
  // if interfaceB, then return canBeTwoInterfaces.B
}

得到最多赞的答案往往是使用“功能检查”。也就是说,

const simpleFunction = (canBeTwoInterfaces: interfaceA | interface B) => { 
  if (canBeTwoInterfaces.onlyExistsOnInterfaceA) return canBeTwoInterfaces.A
  else return canBeTwoInterfaces.B
}

然而,在我正在使用的代码库中,我需要检查的接口主要包含可选参数。另外,我团队里的其他人可能会在我不知情的情况下突然改名字。如果这听起来像您正在使用的代码库,那么下面的函数要安全得多。

就像我之前说的,这对很多人来说可能是一件非常明显的事情。尽管如此,要知道何时何地应用给定的解决方案并不明显,不管它是否恰好像下面这样非常简单。

这就是我要做的:

const simpleFunction = (
  canBeTwoInterfaces: interfaceA | interface B,
  whichInterfaceIsIt: 'interfaceA' | 'interfaceB'
) => { 
  if (whichInterfaceIsIt === 'interfaceA') return canBeTwoInterface.A
  else return canBeTwoInterfaces.B
}

其他回答

在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;
}

它也适用于开关。

在我看来,这是最好的方法;在接口上附加一个“Fubber”符号。它的编写速度要快得多,对于JavaScript引擎来说,它比类型保护快得多,它支持接口的继承,如果你需要的话,它使类型保护易于编写。

这就是ES6有符号的目的。

接口

// Notice there is no naming conflict, because interfaces are a *type*
export const IAnimal = Symbol("IAnimal"); 
export interface IAnimal {
  [IAnimal]: boolean; // the fubber
}

export const IDog = Symbol("IDog");
export interface IDog extends IAnimal {
  [IDog]: boolean;
}

export const IHound = Symbol("IDog");
export interface IHound extends IDog {
  // The fubber can also be typed as only 'true'; meaning it can't be disabled.
  [IDog]: true;
  [IHound]: boolean;
}

import { IDog, IAnimal } from './interfaces';
class Dog implements IDog {
  // Multiple fubbers to handle inheritance:
  [IAnimal] = true;
  [IDog] = true;
}

class Hound extends Dog implements IHound {
  [IHound] = true;
}

测试

如果你想帮助TypeScript编译器,这段代码可以放在类型保护中。

import { IDog, IAnimal } from './interfaces';

let dog = new Dog();

if (dog instanceof Hound || dog[IHound]) {
  // false
}
if (dog[IAnimal]?) {
  // true
}

let houndDog = new Hound();

if (houndDog[IDog]) {
  // true
}

if (dog[IDog]?) {
  // it definitely is a dog
}

这里还有另一个选项:模块ts-interface-builder提供了一个构建时工具,可以将TypeScript接口转换为运行时描述符,ts-interface-checker可以检查对象是否满足它。

对于OP的例子,

interface A {
  member: string;
}

首先运行ts-interface-builder,它会生成一个带有描述符(比如foo-ti)的新简洁文件。Ts,你可以这样用:

import fooDesc from './foo-ti.ts';
import {createCheckers} from "ts-interface-checker";
const {A} = createCheckers(fooDesc);

A.check({member: "hello"});           // OK
A.check({member: 17});                // Fails with ".member is not a string" 

你可以创建一个单行类型保护函数:

function isA(value: any): value is A { return A.test(value); }

TypeGuards

interface MyInterfaced {
    x: number
}

function isMyInterfaced(arg: any): arg is MyInterfaced {
    return arg.x !== undefined;
}

if (isMyInterfaced(obj)) {
    (obj as MyInterfaced ).x;
}

我知道这个问题有点老了,但这只是我的五毛钱。这招对我很管用:

const container: Container = icc.controlComponent as unknown as Container;
if (container.getControlComponents) {
    this.allControlComponents.push(...container.getControlComponents());
}

容器是接口,而icc。controlComponent是我想检查的对象,getControlComponents是来自Container接口的一个方法。